From f0d9ba8415b5c3ba512f15aa27553add7362a56e Mon Sep 17 00:00:00 2001 From: TeamSPoon Date: Tue, 13 Aug 2024 19:56:11 -0700 Subject: [PATCH 1/6] pre stdlib --- scripts/open-metta-file.reg | 29 + src/canary/metta_compiler.pl | 25 +- src/canary/metta_compiler_inlining.pl | 2 +- src/canary/metta_eval.pl | 8 +- src/canary/metta_interp.pl | 8 + src/canary/metta_ontology.pfc.pl | 2 +- src/canary/metta_python.pl | 28 +- src/canary/metta_server.pl | 1 - src/canary/metta_testing.pl | 20 +- src/canary/metta_types.pl | 6 +- src/canary/stdlib.metta | 557 ++++++++++- src/canary/stdlib_mettalog.metta | 892 +++++++++++++++--- src/canary/stdlib_minimal.metta | 858 +++++++++++++---- .../stdlib_mettalog_test.metta | 0 14 files changed, 2075 insertions(+), 361 deletions(-) create mode 100755 scripts/open-metta-file.reg rename {src/canary => tests/more-anti-regression/stdlib-mettalog}/stdlib_mettalog_test.metta (100%) mode change 100644 => 100755 diff --git a/scripts/open-metta-file.reg b/scripts/open-metta-file.reg new file mode 100755 index 00000000000..2c6dfe2ffcf --- /dev/null +++ b/scripts/open-metta-file.reg @@ -0,0 +1,29 @@ +Windows Registry Editor Version 5.00 + +; Associate .metta files with run-metta.cmd +[HKEY_CLASSES_ROOT\.metta] +@="MettaFile" + +[HKEY_CLASSES_ROOT\MettaFile] +@="Metta Script File" + +; Set a system icon for .metta files +[HKEY_CLASSES_ROOT\MettaFile\DefaultIcon] +@="%SystemRoot%\\regedit.exe,0" + +[HKEY_CLASSES_ROOT\MettaFile\shell] +@="open" + +; Default action (open) +[HKEY_CLASSES_ROOT\MettaFile\shell\open] +@="Run with Metta" + +[HKEY_CLASSES_ROOT\MettaFile\shell\open\command] +@="\"H:\\opt\\hyperon\\metta-wam\\mettalog.cmd\" \"%1\"" + +; Add 'Edit with Notepad' to the context menu +[HKEY_CLASSES_ROOT\MettaFile\shell\edit] +@="Edit with Notepad" + +[HKEY_CLASSES_ROOT\MettaFile\shell\edit\command] +@="notepad.exe \"%1\"" \ No newline at end of file diff --git a/src/canary/metta_compiler.pl b/src/canary/metta_compiler.pl index 6c02a45a07c..6abf1abb17a 100755 --- a/src/canary/metta_compiler.pl +++ b/src/canary/metta_compiler.pl @@ -56,7 +56,7 @@ dedupe_p1(P):- current_predicate(_,P), forall((copy_term(P,P2), - clause(P,Bd,Ref), + clause(P,Bd,Ref), clause(P2,Bd2,Ref2), Ref@ FileTime - -> true % PLFile is newer, no need to convert - ; convert_metta_to_pl(File, PlFile) % Replace this with your actual conversion logic - ). - -convert_metta_to_pl(File, PlFile) :- - % Your conversion logic here - % For example: - format('Converting ~w to ~w~n', [File, PlFile]), - convert_metta_to_datalog(File,PlFile). - - compound_name_list(AsPred,FP,PredArgs):- var(AsPred),!,AsPred=[FP|PredArgs]. compound_name_list(AsPred,FP,PredArgs):- iz_conz(AsPred),!,AsPred=[FP|PredArgs]. diff --git a/src/canary/metta_compiler_inlining.pl b/src/canary/metta_compiler_inlining.pl index 800614197c9..851ad26246c 100755 --- a/src/canary/metta_compiler_inlining.pl +++ b/src/canary/metta_compiler_inlining.pl @@ -527,7 +527,7 @@ (( AllCases = Cases, call(ValueCode), once((member(caseOption(MatchVar,MatchCode,BodyResult,BodyCode),AllCases), - both_of(ValueResult,MatchCode,unify_enough(ValueResult,MatchVar)))), + both_of(ValueResult,MatchCode,unify_enough(ValueResult,MatchVar)))), call(BodyCode), BodyResult=RetResult)))). diff --git a/src/canary/metta_eval.pl b/src/canary/metta_eval.pl index 233224ce5d7..e395efe1eac 100755 --- a/src/canary/metta_eval.pl +++ b/src/canary/metta_eval.pl @@ -339,7 +339,8 @@ is_progn('progn'). eval_20(Eq,RetType,Depth,Self,[Comma,X ],Res):- is_progn(Comma),!, eval_args(Eq,RetType,Depth,Self,X,Res). -%eval_20(Eq,RetType,Depth,Self,[Comma,X,Y],Res):- is_progn(Comma),!, eval_args(Eq,_,Depth,Self,X,_),eval_args(Eq,RetType,Depth,Self,Y,Res). +%eval_20(Eq,RetType,Depth,Self,[Comma,X,Y],Res):- is_progn(Comma),!, eval_args(Eq,_,Depth,Self,X,_), +% eval_args(Eq,RetType,Depth,Self,Y,Res). eval_20(Eq,RetType,Depth,Self,[Comma,X|Y],Res):- is_progn(Comma),!, eval_args(Eq,_,Depth,Self,X,_), eval_args(Eq,RetType,Depth,Self,[Comma|Y],Res). @@ -1182,6 +1183,11 @@ eval_20(Eq,RetType,Depth,Self,['charsToString',Chars],String):- !, eval_args(Eq,RetType,Depth,Self,Chars,CC), maplist(as_metta_char,CC0,CC), string_chars(String,CC0). +eval_20(Eq,RetType,_Depth,_Self,['flip'],Bool):- + ignore(RetType='Bool'), !, as_tf(random(0,2,0),Bool), + check_returnval(Eq,RetType,Bool). + + % ================================================================= % ================================================================= % ================================================================= diff --git a/src/canary/metta_interp.pl b/src/canary/metta_interp.pl index e422ec0e9a0..8e8e2fed4b3 100755 --- a/src/canary/metta_interp.pl +++ b/src/canary/metta_interp.pl @@ -605,6 +605,7 @@ % Get Type of Value 'get-type'(Value, Type):- eval_H(['get-type', Value], Type). + % ============================ % %%%% String Utilities % ============================ @@ -612,6 +613,13 @@ 'stringToChars'(String, Chars) :- eval_H(['stringToChars', String], Chars). 'charsToString'(Chars, String) :- eval_H(['charsToString', Chars], String). +% ============================ +% %%%% Random Utilities +% ============================ +'flip'(Bool) :- eval_H(['flip'], Bool). % see `flip` in metta_eval.pl as `eval_20/6` + + + metta_argv(Args):- current_prolog_flag(metta_argv, Args),!. metta_argv(Before):- current_prolog_flag(os_argv,OSArgv), append(_,['--args'|AArgs],OSArgv), before_arfer_dash_dash(AArgs,Before,_),!,set_metta_argv(Before). diff --git a/src/canary/metta_ontology.pfc.pl b/src/canary/metta_ontology.pfc.pl index cdf0924ee01..0c8f156ac30 100755 --- a/src/canary/metta_ontology.pfc.pl +++ b/src/canary/metta_ontology.pfc.pl @@ -461,4 +461,4 @@ % --- String and Character manipulation --- properties('&corelib','stringToChars', [string_operations, qhelp("Convert a string to a list of chars."), string_to_chars]). properties('&corelib','charsToString', [string_operations, qhelp("Convert a list of chars to a string."), chars_to_string]). - +properties('&corelib','flip', [random, qhelp("Return a random boolean."), random_boolean]). diff --git a/src/canary/metta_python.pl b/src/canary/metta_python.pl index f6820778b53..145aabac420 100755 --- a/src/canary/metta_python.pl +++ b/src/canary/metta_python.pl @@ -202,30 +202,31 @@ true. :- endif. -%py_to_pl(VL,Par,_Cir,_,L,_):- pybug(py_to_pl(VL,Par,L)),fail. % py_to_pl/2 - Converts a Python object to a Prolog term. -py_to_pl(I, O) :- py_to_pl(_, I, O). +py_to_pl(I,O):- py_to_pl(_,I,O). % py_to_pl/3 - Calls py_to_pl/6 with initial parameters. -py_to_pl(VL, I, O) :- ignore(VL = [vars]), py_to_pl(VL, [], [], _, I, O), !. +py_to_pl(VL,I,O):- ignore(VL=[vars]), py_to_pl(VL,[],[],_,I,O),!. % is_var_or_nil/1 - Checks if the input is a variable or an empty list. -is_var_or_nil(I) :- var(I), !. +is_var_or_nil(I):- var(I),!. is_var_or_nil([]). % py_to_pl/6 - Main conversion predicate. +% print what we are doing +%py_to_pl(VL,Par,_Cir,_,L,_):- pybug(py_to_pl(VL,Par,L)),fail. % If L is a variable, E is unified with L. -py_to_pl(_VL, _Par, Cir, Cir, L, E) :- var(L), !, E = L. +py_to_pl(_VL,_Par,Cir,Cir,L,E):- var(L),!,E=L. % If O is an object, convert it to Prolog. py_to_pl(VL, Par, Cir, CirO, O, E) :- py_is_object(O), py_class(O, Cl), !, pyo_to_pl(VL, Par, [O = E | Cir], CirO, Cl, O, E). % If L is an empty list, unify E with L. -py_to_pl(_VL, _Par, Cir, Cir, L, E) :- L == [], !, E = L. +py_to_pl(_VL,_Par,Cir,Cir,L,E):- L ==[],!,E=L. % If L is in the Cir list, unify E with L. -py_to_pl(_VL, _Par, Cir, Cir, L, E) :- member(N-NE, Cir), N == L, !, (E = L; NE = E), !. +py_to_pl(_VL,_Par,Cir,Cir,L,E):- member(N-NE,Cir), N==L, !, (E=L;NE=E), !. % If LORV is a variable or nil, unify it directly. -py_to_pl(_VL, _Par, Cir, Cir, LORV:B, LORV:B) :- is_var_or_nil(LORV), !. -py_to_pl(_VL, _Par, Cir, Cir, LORV:_B:_C, LORV) :- is_var_or_nil(LORV), !. +py_to_pl(_VL,_Par,Cir,Cir, LORV:B,LORV:B):- is_var_or_nil(LORV), !. +py_to_pl(_VL,_Par,Cir,Cir, LORV:_B:_C,LORV):- is_var_or_nil(LORV), !. % Convert lists with annotations. py_to_pl(VL, Par, Cir, CirO, [H|T]:B:C, [HH|TT]) :- py_to_pl(VL, Par, Cir, CirM, H:B:C, HH), @@ -254,7 +255,7 @@ py_to_pl(VL, Par, CirM, CirO, B, BB). % If L is an atom, unify E with L. -py_to_pl(_VL, _Par, Cir, Cir, L, E) :- atom(L), !, E = L. +py_to_pl(_VL,_Par,Cir,Cir,L,E):- atom(L),!,E=L. % Convert lists. py_to_pl(VL, Par, Cir, CirO, [H|T], [HH|TT]) :- !, @@ -268,7 +269,9 @@ dict_pairs(E, F, NVL). % If L is not callable, unify E with L. -py_to_pl(_VL, _Par, Cir, Cir, L, E) :- \+ callable(L), !, E = L. +py_to_pl(_VL,_Par,Cir,Cir,L,E):- \+ callable(L),!,E=L. +%py_to_pl(VL,Par,Cir,CirO,A:B:C,AB):- py_is_object(A),callable(B),py_call(A:B,R),!, py_to_pl(VL,Par,[A:B-AB|Cir],CirO,R:C,AB). +%py_to_pl(VL,Par,Cir,CirO,A:B,AB):- py_is_object(A),callable(B),py_call(A:B,R),!, py_to_pl(VL,Par,[A:B-AB|Cir],CirO,R,AB). % Convert compound terms using compound_name_arguments/3. py_to_pl(VL, Par, Cir, CirO, A, AA) :- compound(A), !, @@ -277,8 +280,7 @@ compound_name_arguments(AA, F, LL). % Default case: unify E with E. -py_to_pl(_VL, _Par, Cir, Cir, E, E). - +py_to_pl(_VL,_Par,Cir,Cir,E,E). /* varname_to_real_var(RL,E):- upcase_atom(RL,R),varname_to_real_var0(R,E). varname_to_real_var0(R,E):- nb_current('cvariable_names',VL),!,varname_to_real_var0(R,VL,E). diff --git a/src/canary/metta_server.pl b/src/canary/metta_server.pl index 36581a82841..343d2a5afcd 100755 --- a/src/canary/metta_server.pl +++ b/src/canary/metta_server.pl @@ -63,7 +63,6 @@ ). - service_running(Alias):- thread_property(VSS,TS),VSS=Alias,TS=status(running),!. % Start interpreter service with MSpace = &self diff --git a/src/canary/metta_testing.pl b/src/canary/metta_testing.pl index 7d939258959..0d69486e840 100755 --- a/src/canary/metta_testing.pl +++ b/src/canary/metta_testing.pl @@ -582,26 +582,30 @@ 'atom-count'('&kb22', Count2), writeln(Count2), 'get-atoms'('&kb22', Atoms2), writeln(Atoms2). + %a:- !, be(B), (iF(A,B) -> tHEN(A) ). + %a:- !, be(B), (iF(A,B) *-> tHEN(A) ; eLSE(B) ). + + % Test the code test_my_kb2:- fetch_or_create_space('&kb1', InstanceOfKB), - \+ \+ ('add-atom'('&kb1', a)), - \+ \+ ('add-atom'('&kb1', b)), + \+ \+ ('add-atom'('&kb1', a, Out), writeln(Out)), + \+ \+ ('add-atom'('&kb1', b, Out), writeln(Out)), \+ \+ ('atom-count'('&kb1', Count), writeln(Count)), \+ \+ ('get-atoms'('&kb1', Atoms), writeln(Atoms)), - \+ \+ ('remove-atom'(InstanceOfKB, a)), + \+ \+ ('remove-atom'(InstanceOfKB, a, Out), writeln(Out)), \+ \+ ('get-atoms'('&kb1', NewAtoms), writeln(NewAtoms)), - \+ \+ ('replace-atom'('&kb1', b, c)), + \+ \+ ('replace-atom'('&kb1', b, c, Out), writeln(Out)), \+ \+ ('get-atoms'('&kb1', FinalAtoms), writeln(FinalAtoms)), \+ \+ (space_original_name(InstanceOfKB, OriginalName), writeln(OriginalName)), \+ \+ (fetch_or_create_space('&kb2',_)), % Creating a new space with a different name - \+ \+ ('add-atom'('&kb2', a)), - \+ \+ ('add-atom'('&kb2', b)), + \+ \+ ('add-atom'('&kb2', a, Out), writeln(Out)), + \+ \+ ('add-atom'('&kb2', b, Out), writeln(Out)), \+ \+ ('atom-count'('&kb2', Count), writeln(Count)), \+ \+ ('get-atoms'('&kb2', Atoms), writeln(Atoms)), - \+ \+ ('remove-atom'('&kb2', a)), + \+ \+ ('remove-atom'('&kb2', a, Out), writeln(Out)), \+ \+ ('get-atoms'('&kb2', NewAtoms), writeln(NewAtoms)), - \+ \+ ('replace-atom'('&kb2', b, c)), + \+ \+ ('replace-atom'('&kb2', b, c, Out), writeln(Out)), \+ \+ ('get-atoms'('&kb2', FinalAtoms), writeln(FinalAtoms)). diff --git a/src/canary/metta_types.pl b/src/canary/metta_types.pl index a4a4c672f71..ced20c45e1d 100755 --- a/src/canary/metta_types.pl +++ b/src/canary/metta_types.pl @@ -150,7 +150,7 @@ Type=TypeO, (return_only_first_type->!;true). -return_only_first_type:- fail,true_flag. +return_only_first_type:- true_flag. is_space_type(Space,is_asserted_space):- was_asserted_space(Space),!. is_space_type(Space,Test):- no_repeats(Test,space_type_method(Test,_,_)),call(Test,Space),!. @@ -456,7 +456,7 @@ throw_metta_return(L):- asserta(thrown_metta_return(L)), (throw(metta_return(L))). - + into_typed_args(_Dpth,_Slf,T,M,Y):- (\+ iz_conz(T); \+ iz_conz(M)),!, M=Y. into_typed_args(Depth,Self,[T|TT],[M|MM],[Y|YY]):- @@ -478,7 +478,7 @@ wants_eval_kind(T):- nonvar(T), is_pro_eval_kind(T),!. wants_eval_kind(_):- true. -metta_type:attr_unify_hook(Self=TypeList,NewValue):- +metta_type:attr_unify_hook(Self=TypeList,NewValue):- attvar(NewValue),!,put_attr(NewValue,metta_type,Self=TypeList). metta_type:attr_unify_hook(Self=TypeList,NewValue):- get_type(20,Self,NewValue,Was), diff --git a/src/canary/stdlib.metta b/src/canary/stdlib.metta index cd01380c192..27015103010 100755 --- a/src/canary/stdlib.metta +++ b/src/canary/stdlib.metta @@ -1,12 +1,77 @@ -(@doc = +(@doc = (@desc "A symbol used to define reduction rules for expressions.") (@params ( (@param "Pattern to be matched against expression to be reduced") - (@param "Result of reduction or transformation of the first pattern") ) + (@param "Result of reduction or transformation of the first pattern"))) (@return "Not reduced itself unless custom equalities over equalities are added") ) -) (: = (-> $t $t Atom)) +(@doc cons-atom + (@desc "Constructs an expression using two arguments") + (@params ( + (@param "Head of an expression") + (@param "Tail of an expression"))) + (@return "New expression consists of two input arguments")) + +(@doc println! + (@desc "Prints a line of text to the console") + (@params ( + (@param "Expression/atom to be printed out"))) + (@return "Unit atom")) + +(@doc format-args + (@desc "Fills {} symbols in the input expression with atoms from the second expression. E.g. (format-args (Probability of {} is {}%) (head 50)) gives [(Probability of head is 50%)]. Atoms in the second input value could be variables") + (@params ( + (@param "Expression with {} symbols to be replaced") + (@param "Atoms to be placed inside expression instead of {}"))) + (@return "Expression with replaced {} with atoms")) + +(@doc trace! + (@desc "Prints its first argument and returns second. Both arguments will be evaluated before processing") + (@params ( + (@param "Atom to print") + (@param "Atom to return"))) + (@return "Evaluated second input")) + +(@doc nop + (@desc "Outputs unit atom for any input") + (@params ( + (@param "Anything"))) + (@return "Unit atom")) + +(@doc let + (@desc "Let function is utilized to establish temporary variable bindings within an expression. It allows introducing variables (first argument), assign values to them (second argument), and then use these values within the scope of the let block") + (@params ( + (@param "Variable name (or several variables inside brackets ())") + (@param "Expression to be bound to variable (it is being reduced before bind)") + (@param "Expression which will be reduced and in which variable (first argument) could be used"))) + (@return "Result of third argument's evaluation")) + +(@doc let* + (@desc "Same as let, but first argument is a tuple containing tuples of variables and their bindings, e.g. (($v (+ 1 2)) ($v2 (* 5 6)))") + (@params ( + (@param "Tuple of tuples with variables and their bindings") + (@param "Expression which will be reduced and in which variable (first argument) could be used"))) + (@return "Result of second argument's evaluation")) + + +; TODO: Type is used here, but there is no definition for the -> type +; constructor for instance, thus in practice it matches because -> has +; %Undefined% type. We need to assign proper type to -> and other type +; constructors but it is not possible until we support vararg types. +(@doc is-function-type + (@desc "Function checks if input type is a function type") + (@params ( + (@param "Type notation"))) + (@return "True if input type notation is a function type, False - otherwise")) +(: is-function-type (-> Type Bool)) +(= (is-function-type $type) + (let $type-meta (get-metatype $type) + (case $type-meta ( + (Expression + (let $first (car-atom $type) + (if (== $first ->) True False) )) + ($_ False) )))) (@doc if (@desc "Replace itself by one of the arguments depending on condition.") (@params ( @@ -18,8 +83,6 @@ (= (if True $then $else) $then) (= (if False $then $else) $else) - - (@doc ErrorType (@desc "Type of the atom which contains error")) (: ErrorType Type) @@ -31,8 +94,6 @@ (@return "Error atom")) (: Error (-> Atom Atom ErrorType)) - - (@doc add-reduct (@desc "Adds atom into the atomspace reducing it first") (@params ( @@ -40,14 +101,20 @@ (@param "Atom to add"))) (@return "Unit atom")) (: add-reduct (-> hyperon::space::DynSpace %Undefined% (->))) -(= (add-reduct $dst $atom) (add-atom $dst $atom)) +(= (add-reduct $dst $atom) (add-atom $dst $atom)) -(@doc add-reduct - (@desc "Prevents atom from being reduced") + +(@doc car-atom + (@desc "Extracts the first atom of an expression as a tuple") (@params ( - (@param "Atom"))) - (@return "Quoted atom")) + (@param "Expression"))) + (@return "First atom of an expression")) +(@doc cdr-atom + (@desc "Extracts the tail of an expression (all except first atom)") + (@params ( + (@param "Expression"))) + (@return "Tail of an expression")) (@doc quote (@desc "Prevents atom from being reduced") (@params ( @@ -55,8 +122,14 @@ (@return "Quoted atom")) (: quote (-> Atom Atom)) -; unify matches two atoms and returns $then if they are matched -; and $else otherwise. +(@doc unify + (@desc "Matches two first arguments and returns third argument if they are matched and forth argument otherwise") + (@params ( + (@param "First atom to unify with") + (@param "Second atom to unify with") + (@param "Result if two atoms unified successfully") + (@param "Result otherwise"))) + (@return "Third argument when first two atoms are matched of forth one otherwise")) (: unify (-> Atom Atom Atom Atom %Undefined%)) (= (unify $a $a $then $else) $then) (= (unify $a $b $then $else) @@ -65,7 +138,10 @@ (= (unify-or-empty $a $a) unified) (= (unify-or-empty $a $b) (empty)) -; empty removes current result from a non-deterministic result +(@doc empty + (@desc "Cuts evaluation of the non-deterministic branch and removes it from the result") + (@params ()) + (@return "Nothing")) (: empty (-> %Undefined%)) (= (empty) (let a b never-happens)) @@ -73,23 +149,87 @@ ; Documentation formatting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(@doc @doc + (@desc "Used for documentation purposes. Function documentation starts with @doc") + (@params ( + (@param "Function name") + (@param "Function description. Starts with @desc") + (@param "(Optional) parameters description starting with @params which should contain one or more @param symbols") + (@param "(Optional) description of what function will return. Starts with @return"))) + (@return "Function documentation using @doc-formal")) (: @doc (-> Atom DocDescription DocInformal)) (: @doc (-> Atom DocDescription DocParameters DocReturnInformal DocInformal)) + +(@doc @desc + (@desc "Used for documentation purposes. Description of function starts with @desc as a part of @doc") + (@params ( + (@param "String containing function description"))) + (@return "Function description")) (: @desc (-> String DocDescription)) + +(@doc @param + (@desc "Used for documentation purposes. Description of function parameter starts with @param as a part of @params which is a part of @doc") + (@params ( + (@param "String containing parameter description"))) + (@return "Parameter description")) (: @param (-> String DocParameterInformal)) +(: @param (-> DocType DocDescription DocParameter)) + +(@doc @return + (@desc "Used for documentation purposes. Description of function return value starts with @return as a part of @doc") + (@params ( + (@param "String containing return value description"))) + (@return "Return value description")) (: @return (-> String DocReturnInformal)) +(: @return (-> DocType DocDescription DocReturn)) +(@doc @doc-formal + (@desc "Used for documentation purposes. get-doc returns documentation starting with @doc-formal symbol. @doc-formal contains 6 or 4 parameters depending on the entity being described (functions being described using 6 parameters, atoms - 4 parameters)") + (@params ( + (@param "Function/Atom name for which documentation is to be displayed. Format (@item name)") + (@param "Contains (@kind function) or (@kind atom) depends on entity which documentation is displayed") + (@param "Contains type notation of function/atom") + (@param "Function/atom description") + (@param "(Functions only). Description of function parameters") + (@param "(Functions only). Description of function's return value"))) + (@return "Expression containing full documentation on function")) (: @doc-formal (-> DocItem DocKindFunction DocType DocDescription DocParameters DocReturn DocFormal)) (: @doc-formal (-> DocItem DocKindAtom DocType DocDescription DocFormal)) + +(@doc @item + (@desc "Used for documentation purposes. Converts atom/function's name to DocItem") + (@params ( + (@param "Atom/Function name to be documented"))) + (@return "(@item Atom) entity")) (: @item (-> Atom DocItem)) + +(@doc (@kind function) + (@desc "Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case")) (: (@kind function) DocKindFunction) + +(@doc (@kind atom) + (@desc "Used for documentation purposes. Shows type of entity to be documented. (@kind atom) in this case")) (: (@kind atom) DocKindAtom) + +(@doc @type + (@desc "Used for documentation purposes. Converts atom/function's type to DocType") + (@params ( + (@param "Atom/Function type to be documented"))) + (@return "(@type Type) entity")) (: @type (-> Type DocType)) -(: @params (-> Expression DocParameters)) -(: @param (-> DocType DocDescription DocParameter)) -(: @return (-> DocType DocDescription DocReturn)) +(@doc @params + (@desc "Used for function documentation purposes. Contains several @param entities with description of each @param") + (@params ( + (@param "Several (@param ...) entities"))) + (@return "DocParameters containing description of all parameters of function in form of (@params ((@param ...) (@param ...) ...))")) +(: @params (-> Expression DocParameters)) +(@doc get-doc + (@desc "Returns documentation for the given Atom/Function") + (@params ( + (@param "Atom/Function name for which documentation is needed"))) + (@return "Documentation for the given atom/function")) (: get-doc (-> Atom Atom)) (= (get-doc $atom) (let $meta-type (get-metatype $atom) @@ -97,14 +237,25 @@ (Expression (get-doc-atom $atom)) ($_ (get-doc-single-atom $atom)) )))) +(@doc get-doc-single-atom + (@desc "Function used by get-doc to get documentation on either function or atom. It checks if input name is the name of function or atom and calls correspondent function") + (@params ( + (@param "Atom/Function name for which documentation is needed"))) + (@return "Documentation for the given atom/function")) (: get-doc-single-atom (-> Atom Atom)) (= (get-doc-single-atom $atom) (let $top-space (mod-space! top) (let $type (get-type-space $top-space $atom) (if (is-function-type $type) (get-doc-function $atom $type) - (get-doc-atom $atom) )))) + (get-doc-atom $atom))))) +(@doc get-doc-function + (@desc "Function used by get-doc-single-atom to get documentation on a function. It returns documentation on a function if it exists or default documentation with no description otherwise") + (@params ( + (@param "Function name for which documentation is needed") + (@param "Type notation for this function"))) + (@return "Documentation for the given function")) (: get-doc-function (-> Atom Type Atom)) (= (get-doc-function $name $type) (let $top-space (mod-space! top) @@ -114,6 +265,11 @@ (@doc-formal (@item $name) (@kind function) (@type $type) $desc (@params $params') $ret'))) (@doc-formal (@item $name) (@kind function) (@type $type) (@desc "No documentation")) ))) +(@doc undefined-doc-function-type + (@desc "Function used by get-doc-single-atom in case of absence of function's type notation") + (@params ( + (@param "List of parameters for the function we want to get documentation for"))) + (@return "List of %Undefined% number of which depends on input list size. So for two parameters function will return (%Undefined% %Undefined% %Undefined%)")) (: undefined-doc-function-type (-> Expression Type)) (= (undefined-doc-function-type $params) (if (== () $params) (%Undefined%) @@ -121,6 +277,13 @@ (let $tail (undefined-doc-function-type $params-tail) (cons-atom %Undefined% $tail) )))) +(@doc get-doc-params + (@desc "Function used by get-doc-function to get function's parameters documentation (including return value)") + (@params ( + (@param "List of parameters in form of ((@param Description) (@param Description)...)") + (@param "Return value's description in form of (@return Description)") + (@param "Type notation without -> starting symbol e.g. (Atom Atom Atom)"))) + (@return "United list of params and return value each augmented with its type. E.g. (((@param (@type Atom) (@desc Description)) (@param (@type Atom) (@desc Description2))) (@return (@type Atom) (@desc Description)))")) (: get-doc-params (-> Expression Atom Expression (Expression Atom))) (= (get-doc-params $params $ret $types) (let $head-type (car-atom $types) @@ -134,6 +297,11 @@ (let $result-params (cons-atom (@param (@type $head-type) (@desc $param-desc)) $params') ($result-params $result-ret) )))))))) +(@doc get-doc-atom + (@desc "Function used by get-doc (in case of input type Expression) and get-doc-single-atom (in case input value is not a function) to get documentation on input value") + (@params ( + (@param "Atom's name to get documentation for"))) + (@return "Documentation on input Atom")) (: get-doc-atom (-> Atom Atom)) (= (get-doc-atom $atom) (let $top-space (mod-space! top) @@ -144,21 +312,11 @@ (get-doc-function $atom %Undefined%) (@doc-formal (@item $atom) (@kind atom) (@type $type) (@desc "No documentation")) ))))) -; TODO: Type is used here, but there is no definition for the -> type -; constructor for instance, thus in practice it matches because -> has -; %Undefined% type. We need to assign proper type to -> and other type -; constructors but it is not possible until we support vararg types. -(: is-function-type (-> Type Bool)) -(= (is-function-type $type) - (let $type-meta (get-metatype $type) - (case $type-meta ( - (Expression - (let $first (car-atom $type) - (if (== $first ->) True False) )) - ($_ False) )))) - - - +(@doc help! + (@desc "Function prints documentation for the input atom.") + (@params ( + (@param "Input to get documentation for"))) + (@return "Unit atom")) (: help! (-> Atom (->))) (= (help! $atom) (case (get-doc $atom) ( @@ -178,11 +336,22 @@ () )) ($other (Error $other "Cannot match @doc-formal structure") )))) +(@doc help-param! + (@desc "Function used by function help! to output parameters using println!") + (@params ( + (@param "Parameters list"))) + (@return "Unit atom")) (: help-param! (-> Atom (->))) (= (help-param! $param) (let (@param (@type $type) (@desc $desc)) $param - (println! (format-args " {} {}" ($type $desc))) )) + (println! (format-args " {} {}" ((type $type) $desc))) )) +(@doc for-each-in-atom + (@desc "Applies function passed as a second argument to each atom inside first argument") + (@params ( + (@param "Expression to each atom in which function will be applied") + (@param "Function to apply"))) + (@return "Unit atom")) (: for-each-in-atom (-> Expression Atom (->))) (= (for-each-in-atom $expr $func) (if (noreduce-eq $expr ()) @@ -192,6 +361,12 @@ (let $_ ($func $head) (for-each-in-atom $tail $func) ))))) +(@doc noreduce-eq + (@desc "Checks equality of two atoms without reducing them") + (@params ( + (@param "First atom") + (@param "Second atom"))) + (@return "True if not reduced atoms are equal, False - otherwise")) (: noreduce-eq (-> Atom Atom Bool)) (= (noreduce-eq $a $b) (== (quote $a) (quote $b))) @@ -205,3 +380,315 @@ (@param "Atomspace to add atom into") (@param "Atom to add"))) (@return "Unit atom")) + +(@doc match + (@desc "Searches for all declared atoms corresponding to the given pattern (second argument) and produces the output pattern (third argument)") + (@params ( + (@param "A grounded atom referencing a Space") + (@param "Pattern atom to be matched") + (@param "Output pattern typically containing variables from the input pattern"))) + (@return "If match was successfull it outputs pattern (third argument) with filled variables (if any were present in pattern) using matched pattern (second argument). Nothing - otherwise")) + +(@doc bind! + (@desc "Registers a new token which is replaced with an atom during the parsing of the rest of the program") + (@params ( + (@param "Token name") + (@param "Atom, which is associated with the token after reduction"))) + (@return "Unit atom")) + +(@doc new-space + (@desc "Creates new Atomspace which could be used further in the program as a separate from &self Atomspace") + (@params ()) + (@return "Reference to a new space")) + +(@doc remove-atom + (@desc "Removes atom from the input Atomspace") + (@params ( + (@param "Reference to the space from which the Atom needs to be removed") + (@param "Atom to be removed"))) + (@return "Unit atom")) + +(@doc get-atoms + (@desc "Shows all atoms in the input Atomspace") + (@params ( + (@param "Reference to the space"))) + (@return "List of all atoms in the input space")) + +(@doc new-state + (@desc "Creates a new state atom wrapping its argument") + (@params ( + (@param "Atom to be wrapped"))) + (@return "Returns (State $value) where $value is an argument to a new-state")) + +(@doc change-state! + (@desc "Changes input state's wrapped atom to another value (second argument). E.g. (change-state! (State 5) 6) -> (State 6)") + (@params ( + (@param "State created by new-state function") + (@param "Atom which will replace wrapped atom in the input state"))) + (@return "State with replaced wrapped atom")) + +(@doc get-state + (@desc "Gets a state as an argument and returns its wrapped atom. E.g. (get-state (State 5)) -> 5") + (@params ( + (@param "State"))) + (@return "Atom wrapped by state")) + +(@doc get-metatype + (@desc "Returns metatype of the input atom") + (@params ( + (@param "Atom to get metatype for"))) + (@return "Metatype of input atom")) + +(@doc register-module! + (@desc "Takes a file system path (first argument) and loads the module into the runner") + (@params ( + (@param "File system path"))) + (@return "Unit atom")) + +(@doc mod-space! + (@desc "Returns the space of the module (first argument) and tries to load the module if it is not loaded into the module system") + (@params ( + (@param "Module name"))) + (@return "Space name")) + +(@doc print-mods! + (@desc "Prints all modules with their correspondent spaces") + (@params ()) + (@return "Unit atom")) + +(@doc sealed + (@desc "Replaces all occurrences of any var from var list (first argument) inside atom (second argument) by unique variable. Can be used to create a locally scoped variables") + (@params ( + (@param "Variable list e.g. ($x $y)") + (@param "Atom which uses those variables"))) + (@return "Second argument but with variables being replaced with unique variables")) + +(@doc assertEqual + (@desc "Compares (sets of) results of evaluation of two expressions") + (@params ( + (@param "First expression") + (@param "Second expression"))) + (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + +(@doc assertEqualToResult + (@desc "Same as assertEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") + (@params ( + (@param "First expression (it will be evaluated)") + (@param "Second expression (it won't be evaluated)"))) + (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + +(@doc collapse + (@desc "Converts a nondeterministic result into a tuple") + (@params ( + (@param "Atom which will be evaluated"))) + (@return "Tuple")) + +(@doc capture + (@desc "Wraps an atom and capture the current space") + (@params ( + (@param "Function name which space need to be captured"))) + (@return "Function")) + +(@doc case + (@desc "Subsequently tests multiple pattern-matching conditions (second argument) for the given value (first argument)") + (@params ( + (@param "Atom (it will be evaluated)") + (@param "Tuple of pairs mapping condition patterns to results"))) + (@return "Result of evaluating of Atom bound to met condition")) + + + +(@doc superpose + (@desc "Turns a tuple (first argument) into a nondeterministic result") + (@params ( + (@param "Tuple to be converted"))) + (@return "Argument converted to nondeterministic result")) + +(@doc get-type + (@desc "Returns type notation of input atom") + (@params ( + (@param "Atom to get type for"))) + (@return "Type notation or %Undefined% if there is no type for input Atom")) + +(@doc get-type-space + (@desc "Returns type notation of input Atom (second argument) relative to a specified atomspace (first argument)") + (@params ( + (@param "Atomspace where type notation for input atom will be searched") + (@param "Atom to get type for"))) + (@return "Type notation or %Undefined% if there is no type for input Atom in provided atomspace")) + +(@doc import! + (@desc "Imports module using its relative path (second argument) and binds it to the token (first argument) which will represent imported atomspace. If first argument is &self then everything will be imported to current atomspace") + (@params ( + (@param "Symbol, which is turned into the token for accessing the imported module") + (@param "Module name"))) + (@return "Unit atom")) + +(@doc include + (@desc "Works just like import! but with &self as a first argument. So everything from input file will be included in the current atomspace and evaluated") + (@params ( + (@param "Name of metta script to import"))) + (@return "Unit atom")) + + +(@doc pragma! + (@desc "Changes global key's (first argument) value to a new one (second argument)") + (@params ( + (@param "Key's name") + (@param "New value"))) + (@return "Unit atom")) + + +; TODO: Segmentation fault (core dumped) when calling !(help &self) +;(@doc &self +; (@desc "Returns reference to the current atomspace") +; (@params ()) +; (@return "Reference to the current atomspace")) + +; TODO: get-doc/help! not working for + +(@doc + + (@desc "Sums two numbers") + (@params ( + (@param "Addend") + (@param "Augend"))) + (@return "Sum")) + +; TODO: get-doc/help! not working for - +(@doc - + (@desc "Subtracts second argument from first one") + (@params ( + (@param "Minuend") + (@param "Deductible"))) + (@return "Difference")) + +; TODO: get-doc/help! not working for * +(@doc * + (@desc "Multiplies two numbers") + (@params ( + (@param "Multiplier") + (@param "Multiplicand"))) + (@return "Product")) + +; TODO: get-doc/help! not working for / +(@doc / + (@desc "Divides first argument by second one") + (@params ( + (@param "Dividend") + (@param "Divisor"))) + (@return "Fraction")) + +; TODO: get-doc/help! not working for % +(@doc % + (@desc "Modulo operator. It returns remainder of dividing first argument by second argument") + (@params ( + (@param "Dividend") + (@param "Divisor"))) + (@return "Remainder")) + +; TODO: get-doc/help! not working for < +(@doc < + (@desc "Less than. Checks if first argument is less than second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is less than second, False - otherwise")) + +; TODO: get-doc/help! not working for > +(@doc > + (@desc "Greater than. Checks if first argument is greater than second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is greater than second, False - otherwise")) + +; TODO: get-doc/help! not working for <= +(@doc <= + (@desc "Less than or equal. Checks if first argument is less than or equal to second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is less than or equal to second, False - otherwise")) + +; TODO: get-doc/help! not working for >= +(@doc >= + (@desc "Greater than or equal. Checks if first argument is greater than or equal to second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is greater than or equal to second, False - otherwise")) + +; TODO: get-doc/help! not working for == +(@doc == + (@desc "Checks equality for two arguments of the same type") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Returns True if two arguments are equal, False - otherwise. If arguments are of different type function returns Error currently")) + +; TODO: get-doc/help! not working for and +(@doc and + (@desc "Logical conjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Returns True if both arguments are True, False - otherwise")) + +; TODO: get-doc/help! not working for or +(@doc or + (@desc "Logical disjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "True if any of input arguments is True, False - otherwise")) + +; TODO: get-doc/help! not working for not +(@doc not + (@desc "Negation") + (@params ( + (@param "Argument"))) + (@return "Negates boolean input argument (False -> True, True -> False)")) + +(@doc xor + (@desc "Exclusive disjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Return values are the same as logical disjunction, but when both arguments are True xor will return False")) + +(@doc flip + (@desc "Produces random boolean value") + (@params ()) + (@return "Random boolean value")) + +(@doc unique + (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") + (@params ( + (@param "Non-deterministic set of values"))) + (@return "Unique values from input set")) + +(@doc union + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Union of sets")) + +(@doc intersection + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Intersection of sets")) + +(@doc subtraction + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Subtraction of sets")) + +(@doc git-module! + (@desc "Provides access to module in a remote git repo, from within MeTTa code. Similar to `register-module!`, this op will bypass the catalog search") + (@params ( + (@param "URL to github repo"))) + (@return "Unit atom")) \ No newline at end of file diff --git a/src/canary/stdlib_mettalog.metta b/src/canary/stdlib_mettalog.metta index d98c7825cd2..b3914b24247 100755 --- a/src/canary/stdlib_mettalog.metta +++ b/src/canary/stdlib_mettalog.metta @@ -2,49 +2,141 @@ (@desc "A symbol used to define reduction rules for expressions.") (@params ( (@param "Pattern to be matched against expression to be reduced") - (@param "Result of reduction or transformation of the first pattern") ) + (@param "Result of reduction or transformation of the first pattern"))) (@return "Not reduced itself unless custom equalities over equalities are added") ) -) (: = (-> $t $t Atom)) - (@doc ErrorType (@desc "Type of the atom which contains error")) (: ErrorType Type) + +(@doc Error + (@desc "Error constructor") + (@params ( + (@param "Atom which contains error") + (@param "Error message, can be one of the reserved symbols: BadType, IncorrectNumberOfArguments"))) + (@return "Instance of the error atom")) (: Error (-> Atom Atom ErrorType)) + + +(@doc return + (@desc "Returns value from the (function ...) expression") + (@params ( + (@param "Value to be returned"))) + (@return "Passed argument")) (: return (-> $t $t)) +(@doc function + (@desc "Evaluates the argument until it becomes (return ). Then (function (return )) is reduced to the .") + (@params ( + (@param "Atom to be evaluated"))) + (@return "Result of atom's evaluation")) (: function (-> Atom Atom)) + +(@doc eval + (@desc "Evaluates input atom, makes one step of the evaluation") + (@params ( + (@param "Atom to be evaluated, can be reduced via equality expression (= ...) or by calling a grounded function"))) + (@return "Result of evaluation")) (: eval (-> Atom Atom)) + +(@doc chain + (@desc "Evaluates first argument, binds it to the variable (second argument) and then evaluates third argument which contains (or not) mentioned variable") + (@params ( + (@param "Atom to be evaluated") + (@param "Variable") + (@param "Atom which will be evaluated at the end"))) + (@return "Result of evaluating third input argument")) (: chain (-> Atom Variable Atom Atom)) + +(@doc unify + (@desc "Matches two first arguments and returns third argument if they are matched and forth argument otherwise") + (@params ( + (@param "First atom to unify with") + (@param "Second atom to unify with") + (@param "Result if two atoms unified successfully") + (@param "Result otherwise"))) + (@return "Third argument when first two atoms are matched of forth one otherwise")) (: unify (-> Atom Atom Atom Atom Atom)) +(: unify (-> Atom Atom Atom Atom %Undefined%)) +(= (unify $a $a $then $else) $then) +(= (unify $a $b $then $else) + (case (unify-or-empty $a $b) ((Empty $else))) ) -(: cons (-> Atom Atom Atom)) -; We specialize but leaving the old defs in case +(: unify-or-empty (-> Atom Atom Atom)) +(= (unify-or-empty $a $a) unified) +(= (unify-or-empty $a $b) (empty)) + + +(@doc cons-atom + (@desc "Constructs an expression using two arguments") + (@params ( + (@param "Head of an expression") + (@param "Tail of an expression"))) + (@return "New expression consists of two input arguments")) (: cons-atom (-> Atom Expression Expression)) + +(: cons (-> Atom Atom Atom)) (: decons (-> Atom Atom)) -; We specialize but leaving the old defs in case + +(@doc decons-atom + (@desc "Works as a reverse to cons-atom function. It gets Expression as an input and returns it splitted to head and tail, e.g. (decons-atom (Cons X Nil)) -> (Cons (X Nil))") + (@params ( + (@param "Expression"))) + (@return "Deconsed expression")) (: decons-atom (-> Expression Expression)) +(@doc collapse-bind + (@desc "Evaluates the Atom (first argument) and returns an expression which contains all alternative evaluations in a form (Atom Bindings). Bindings are represented in a form of a grounded atom.") + (@params ( + (@param "Atom to be evaluated"))) + (@return "All alternative evaluations")) ;; collapse-bind because `collapse` doesnt guarentee shared bindings -(: collapse-bind (-> Atom Atom)) -; We specialize but leaving the old defs in case +(: collapse-bind (-> Atom Atom)) ; We specialize but leaving the old defs in case (: collapse-bind (-> Atom Expression)) - +(@doc superpose-bind + (@desc "Complement to the collapse-bind. It takes result of collapse-bind (first argument) and returns only result atoms without bindings") + (@params ( + (@param "Expression in form (Atom Binding)"))) + (@return "Non-deterministic list of Atoms")) ;; superpose-bind because `superpose` doesnt guarentee shared bindings -(: superpose-bind (-> Atom Atom)) -; We specialize but leaving the old defs in case +(: superpose-bind (-> Atom Atom)) ; We specialize them but leaving the old defs in case (: superpose-bind (-> Expression Atom)) - +(@doc metta + (@desc "Run MeTTa interpreter on atom.") + (@params ( + (@param "Atom to be interpreted") + (@param "Type of input atom") + (@param "Atomspace where intepretation should take place"))) + (@return "Result of interpretation")) +(: metta (-> Atom Type Grounded Atom)) + +(@doc id + (@desc "Returns its argument") + (@params ( + (@param "Input argument"))) + (@return "Input argument")) (: id (-> Atom Atom)) (= (id $x) $x) -(: apply (-> Atom Variable Atom Atom)) -(= (apply $atom $var $templ) - (function (chain (eval (id $atom)) $var (return $templ))) ) - +(@doc atom-subst + (@desc "Substitutes variable passed as a second argument in the third argument by the first argument") + (@params ( + (@param "Value to use for replacement") + (@param "Variable to replace") + (@param "Template to replace variable by the value"))) + (@return "Template with substituted variable")) (: atom-subst (-> Atom Variable Atom Atom)) (= (atom-subst $atom $var $templ) (function (chain (eval (id $atom)) $var (return $templ))) ) +(@doc if-decons-expr + (@desc "Checks if first argument is non empty expression. If so gets tail and head from the first argument and returns forth argument using head and tail values. Returns fifth argument otherwise.") + (@params ( + (@param "Expression to be deconstructed") + (@param "Head variable") + (@param "Tail variable") + (@param "Template to return if first argument is a non-empty expression") + (@param "Default value to return otherwise"))) + (@return "Either template with head and tail replaced by values or default value")) (: if-decons-expr (-> Expression Variable Variable Atom Atom Atom)) (= (if-decons-expr $atom $head $tail $then $else) (function (eval (if-equal $atom () @@ -52,6 +144,13 @@ (chain (decons-atom $atom) $list (unify $list ($head $tail) (return $then) (return $else)) ))))) +(@doc if-error + (@desc "Checks if first argument is an error atom. Returns second argument if so or third argument otherwise.") + (@params ( + (@param "Atom to be checked for the error") + (@param "Value to return if first argument is an error") + (@param "Value to return otherwise"))) + (@return "Second or third argument")) (: if-error (-> Atom Atom Atom Atom)) (= (if-error $atom $then $else) (function (chain (eval (get-metatype $atom)) $meta @@ -64,6 +163,12 @@ (return $else) )))) (return $else) ))))) +(@doc return-on-error + (@desc "Returns first argument if it is Empty or an error. Returns second argument otherwise.") + (@params ( + (@param "Previous evaluation result") + (@param "Atom for further evaluation"))) + (@return "Return previous result if it is an error or Empty or continue evaluation")) (: return-on-error (-> Atom Atom Atom)) (= (return-on-error $atom $then) (function (eval (if-equal $atom Empty (return (return Empty)) @@ -79,20 +184,34 @@ ; (for example user evaluates `!(switch (unify A B ok Empty) ...)` then ; emptiness of the first argument is checked by interpreter and it will ; break execution when `Empty` is returned. +(@doc switch + (@desc "Subsequently tests multiple pattern-matching conditions (second argument) for the given value (first argument)") + (@params ( + (@param "Atom to be matched with patterns") + (@param "Tuple of pairs mapping condition patterns to results"))) + (@return "Result which corresponds to the pattern which is matched with the passed atom first")) -(: switch (-> Atom Atom Atom)) -(: ( + +(: switch (-> Atom Atom Atom)) (= (switch $atom $list) (case (eval $atom) $list)) +(: ( +(: switch (-> %Undefined% Expression Atom)) (= (switch $atom $cases) (function (chain (decons-atom $cases) $list (chain (eval (switch-internal $atom $list)) $res (chain (eval (if-equal $res NotReducible Empty $res)) $x (return $x)) )))) +(@doc switch-internal + (@desc "This function is being called inside switch function to test one of the cases and it calls switch once again if current condition is not met") + (@params ( + (@param "Atom (it will be evaluated)") + (@param "Deconsed tuple of pairs mapping condition patterns to results"))) + (@return "Result of evaluating of Atom bound to met condition")) (= (switch-internal $atom (($pattern $template) $tail)) (function (unify $atom $pattern (return $template) @@ -103,10 +222,52 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; MeTTa interpreter implementation ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; TODO: Type is used here, but there is no definition for the -> type +; constructor for instance, thus in practice it matches because -> has +; %Undefined% type. We need to assign proper type to -> and other type +; constructors but it is not possible until we support vararg types. +(@doc is-function + (@desc "Function checks if input type is a function type") + (@params ( + (@param "Type atom"))) + (@return "True if type is a function type, False - otherwise")) +(: is-function (-> Type Bool)) +(= (is-function $type) + (function (chain (eval (get-metatype $type)) $meta + (eval (switch ($type $meta) ( + (($type Expression) + (eval (if-decons-expr $type $head $_tail + (unify $head -> (return True) (return False)) + (return (Error (is-function $type) "is-function non-empty expression as an argument")) ))) + (($type $meta) (return False)) + )))))) +(@doc type-cast + (@desc "Casts atom passed as a first argument to the type passed as a second argument using space as a context") + (@params ( + (@param "Atom to be casted") + (@param "Type to cast atom to") + (@param "Context atomspace"))) + (@return "Atom if casting is successful, (Error ... BadType) otherwise")) +(= (type-cast $atom $type $space) + (function (chain (eval (get-metatype $atom)) $meta + (eval (if-equal $type $meta + (return $atom) + (chain (eval (collapse-bind (eval (get-type $atom $space)))) $collapsed + (chain (eval (map-atom $collapsed $pair (eval (first-from-pair $pair)))) $actual-types + (chain (eval (foldl-atom $actual-types False $a $b (eval (match-type-or $a $b $type)))) $is-some-comp + (eval (if $is-some-comp + (return $atom) + (return (Error $atom BadType)) )))))))))) + +(@doc match-types + (@desc "Checks if two types can be unified and returns third argument if so, fourth - otherwise") + (@params ( + (@param "First type") + (@param "Second type") + (@param "Atom to be returned if types can be unified") + (@param "Atom to be returned if types cannot be unified"))) + (@return "Third or fourth argument")) (= (match-types $type1 $type2 $then $else) (function (eval (if-equal $type1 %Undefined% (return $then) @@ -118,44 +279,36 @@ (return $then) (unify $type1 $type2 (return $then) (return $else)) )))))))))) +(@doc first-from-pair + (@desc "Gets a pair as a first argument and returns first atom from pair") + (@params ( + (@param "Pair"))) + (@return "First atom from a pair")) (= (first-from-pair $pair) (function (unify $pair ($first $second) (return $first) (return (Error (first-from-pair $pair) "incorrect pair format"))))) +(@doc match-type-or + (@desc "Checks if two types (second and third arguments) can be unified and returns result of OR operation between first argument and type checking result") + (@params ( + (@param "Boolean value") + (@param "First type") + (@param "Second type"))) + (@return "True or False")) (= (match-type-or $folded $next $type) (function (chain (eval (match-types $next $type True False)) $matched (chain (eval (or $folded $matched)) $or (return $or)) ))) -(= (type-cast $atom $type $space) - (function (chain (eval (get-metatype $atom)) $meta - (eval (if-equal $type $meta - (return $atom) - (chain (eval (collapse-bind (eval (get-type $atom $space)))) $collapsed - (chain (eval (map-atom $collapsed $pair (eval (first-from-pair $pair)))) $actual-types - (chain (eval (foldl-atom $actual-types False $a $b (eval (match-type-or $a $b $type)))) $is-some-comp - (eval (if $is-some-comp - (return $atom) - (return (Error $atom BadType)) )))))))))) - - -; TODO: Type is used here, but there is no definition for the -> type -; constructor for instance, thus in practice it matches because -> has -; %Undefined% type. We need to assign proper type to -> and other type -; constructors but it is not possible until we support vararg types. -(: is-function (-> Type Bool)) -(= (is-function $type) - (function (chain (eval (get-metatype $type)) $meta - (eval (switch ($type $meta) ( - (($type Expression) - (eval (if-decons-expr $type $head $_tail - (unify $head -> (return True) (return False)) - (return (Error (is-function $type) "is-function non-empty expression as an argument")) ))) - (($type $meta) (return False)) - )))))) - +(@doc filter-atom + (@desc "Function takes list of atoms (first argument), variable (second argument) and filter predicate (third argument) and returns list with items which passed filter. E.g. (filter-atom (1 2 3 4) $v (eval (> $v 2))) will give (3 4)") + (@params ( + (@param "List of atoms") + (@param "Variable") + (@param "Filter predicate"))) + (@return "Filtered list")) (: filter-atom (-> Expression Variable Atom Expression)) (= (filter-atom $list $var $filter) (function (eval (if-decons-expr $list $head $tail @@ -167,6 +320,13 @@ (return $tail-filtered) ))))) (return ()) )))) +(@doc map-atom + (@desc "Function takes list of atoms (first argument), variable to be used inside (second variable) and an expression which will be evaluated for each atom in list (third argument). Expression should contain variable. So e.g. (map-atom (1 2 3 4) $v (eval (+ $v 1))) will give (2 3 4 5)") + (@params ( + (@param "List of atoms") + (@param "Variable name") + (@param "Template using variable"))) + (@return "Result of evaluating template for each atom in a list")) (: map-atom (-> Expression Variable Atom Expression)) (= (map-atom $list $var $map) (function (eval (if-decons-expr $list $head $tail @@ -176,6 +336,15 @@ (chain (cons-atom $head-mapped $tail-mapped) $res (return $res)) ))) (return ()) )))) +(@doc foldl-atom + (@desc "Function takes list of values (first argument), initial value (second argument) and operation (fifth argument) and applies it consequently to the list of values, using init value as a start. It also takes two variables (third and fourth argument) to use them inside") + (@params ( + (@param "List of values") + (@param "Init value") + (@param "Variable") + (@param "Variable") + (@param "Operation"))) + (@return "Result of applying operation to the list of values")) (: foldl-atom (-> Expression Atom Variable Variable Atom Atom)) (= (foldl-atom $list $init $a $b $op) (function (eval (if-decons-expr $list $head $tail @@ -294,6 +463,47 @@ ; Standard library written in MeTTa ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(@doc if-equal + (@desc "Checks if first two arguments are equal and evaluates third argument if equal, fourth argument - otherwise") + (@params ( + (@param "First argument") + (@param "Second argument") + (@param "Atom to be evaluated if arguments are equal") + (@param "Atom to be evaluated if arguments are not equal"))) + (@return "Evaluated third or fourth argument")) + +; TODO: Type is used here, but there is no definition for the -> type +; constructor for instance, thus in practice it matches because -> has +; %Undefined% type. We need to assign proper type to -> and other type +; constructors but it is not possible until we support vararg types. +(@doc is-function-type + (@desc "Function checks if input type is a function type") + (@params ( + (@param "Type notation"))) + (@return "True if input type notation is a function type, False - otherwise")) +(: is-function-type (-> Type Bool)) +(= (is-function-type $type) + (let $type-meta (get-metatype $type) + (case $type-meta ( + (Expression + (let $first (car-atom $type) + (if (== $first ->) True False) )) + ($_ False) )))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MeTTa interpreter implementation ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(: apply (-> Atom Variable Atom Atom)) +(= (apply $atom $var $templ) + (function (chain (eval (id $atom)) $var (return $templ))) ) + + + + (@doc if (@desc "Replace itself by one of the arguments depending on condition.") (@params ( @@ -309,44 +519,92 @@ -(@doc ErrorType (@desc "Type of the atom which contains error")) -(: ErrorType Type) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; MeTTa interpreter implementation ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(@doc Error - (@desc "Error constructor") +(: apply (-> Atom Variable Atom Atom)) +(= (apply $atom $var $templ) + (function (chain (eval (id $atom)) $var (return $templ))) ) + + + +; TODO: help! not working for operations which are defined in both Python and +;Rust standard library: or,and,not + +(@doc xor + (@desc "Exclusive disjunction of two arguments") (@params ( - (@param "Atom which contains error") - (@param "Error message, can be one of the reserved symbols: BadType, IncorrectNumberOfArguments"))) - (@return "Error atom")) -(: Error (-> Atom Atom ErrorType)) + (@param "First argument") + (@param "Second argument"))) + (@return "Return values are the same as logical disjunction, but when both arguments are True xor will return False")) + +(@doc flip + (@desc "Produces random boolean value") + (@params ()) + (@return "Random boolean value")) + +(: match (-> Atom Atom Atom %Undefined%)) +;(= (match $space $pattern $template) +; (unify $pattern $space $template Empty)) + + +(@doc or + (@desc "Logical disjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "True if any of input arguments is True, False - otherwise")) (: or (-> Bool Bool Bool)) (= (or False False) False) (= (or False True) True) (= (or True False) True) (= (or True True) True) + +(@doc and + (@desc "Logical conjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Returns True if both arguments are True, False - otherwise")) (: and (-> Bool Bool Bool)) (= (and False False) False) (= (and False True) False) (= (and True False) False) (= (and True True) True) +(@doc not + (@desc "Logical negation") + (@params ( + (@param "Argument"))) + (@return "Negates boolean input argument (False -> True, True -> False)")) (: not (-> Bool Bool)) (= (not True) False) (= (not False) True) -(: match (-> Atom Atom Atom %Undefined%)) -;(= (match $space $pattern $template) -; (unify $pattern $space $template Empty)) +(@doc let + (@desc "Let function is utilized to establish temporary variable bindings within an expression. It allows introducing variables (first argument), assign values to them (second argument), and then use these values within the scope of the let block") + (@params ( + (@param "Variable name (or several variables inside brackets ())") + (@param "Expression to be bound to variable (it is being reduced before bind)") + (@param "Expression which will be reduced and in which variable (first argument) could be used"))) + (@return "Result of third argument's evaluation")) (: let (-> Atom %Undefined% Atom Atom)) (= (let $pattern $atom $template) (unify $atom $pattern $template Empty)) +(@doc let* + (@desc "Same as let, but first argument is a tuple containing tuples of variables and their bindings, e.g. (($v (+ 1 2)) ($v2 (* 5 6)))") + (@params ( + (@param "Tuple of tuples with variables and their bindings") + (@param "Expression which will be reduced and in which variable (first argument) could be used"))) + (@return "Result of second argument's evaluation")) (: let* (-> Expression Atom Atom)) (= (let* $pairs $template) (eval (if-decons-expr $pairs ($pattern $atom) $tail @@ -355,8 +613,17 @@ +(:> Space Grounded) +(= (mod-space! top) &self) +(= (get-type-space $space $atom) + (get-type $atom $space)) +(@doc add-reduct + (@desc "Prevents atom from being reduced") + (@params ( + (@param "Atom"))) + (@return "Quoted atom")) (@doc add-reduct-nm (@desc "Adds atom into the atomspace reducing it first") (@params ( @@ -365,55 +632,66 @@ (@return "Unit atom")) (: add-reduct-nm (-> Space %Undefined% (->))) -(= (add-reduct-minimal $dst $atom) - (add-atom $dst $atom)) - -(:> Space Grounded) - -(@doc add-reduct - (@desc "Prevents atom from being reduced") - (@params ( - (@param "Atom"))) - (@return "Quoted atom")) +(= (add-reduct-minimal $dst $atom) (add-atom $dst $atom)) (: add-reduct (-> Grounded %Undefined% (->))) (= (add-reduct $dst $atom) (add-atom $dst $atom)) + + +(@doc car-atom + (@desc "Extracts the first atom of an expression as a tuple") + (@params ( + (@param "Expression"))) + (@return "First atom of an expression")) + (: car-atom (-> Expression Atom)) (= (car-atom $atom) (eval (if-decons-expr $atom $head $_ $head (Error (car-atom $atom) "car-atom expects a non-empty expression as an argument") ))) + +(@doc cdr-atom + (@desc "Extracts the tail of an expression (all except first atom)") + (@params ( + (@param "Expression"))) + (@return "Tail of an expression")) (: cdr-atom (-> Expression Expression)) (= (cdr-atom $atom) (eval (if-decons-expr $atom $_ $tail $tail (Error (cdr-atom $atom) "cdr-atom expects a non-empty expression as an argument") ))) +(@doc quote + (@desc "Prevents atom from being reduced") + (@params ( + (@param "Atom"))) + (@return "Quoted atom")) (: quote (-> Atom Atom)) (= (quote $atom) NotReducible) +(@doc unquote + (@desc "Unquotes quoted atom, e.g. (unquote (quote $x)) returns $x") + (@params ( + (@param "Quoted atom"))) + (@return "Unquoted atom")) (: unquote (-> %Undefined% %Undefined%)) (= (unquote (quote $atom)) $atom) ; TODO: there is no way to define operation which consumes any number of ; arguments and returns unit +(@doc nop + (@desc "Outputs unit atom for any input") + (@params ( + (@param "Anything"))) + (@return "Unit atom")) (= (nop) ()) (= (nop $x) ()) - -; unify matches two atoms and returns $then if they are matched -; and $else otherwise. -(: unify (-> Atom Atom Atom Atom %Undefined%)) -(= (unify $a $a $then $else) $then) -(= (unify $a $b $then $else) - (case (unify-or-empty $a $b) ((%void% $else))) ) - -(: unify-or-empty (-> Atom Atom Atom)) -(= (unify-or-empty $a $a) unified) -(= (unify-or-empty $a $b) (empty)) - -; empty removes current result from a non-deterministic result +(@doc empty + (@desc "Cuts evaluation of the non-deterministic branch and removes it from the result") + (@params ()) + (@return "Nothing")) (: empty (-> %Undefined%)) (= (empty) (let a b never-happens)) @@ -425,26 +703,90 @@ ; Documentation formatting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(@doc @doc + (@desc "Used for documentation purposes. Function documentation starts with @doc") + (@params ( + (@param "Function name") + (@param "Function description. Starts with @desc") + (@param "(Optional) parameters description starting with @params which should contain one or more @param symbols") + (@param "(Optional) description of what function will return. Starts with @return"))) + (@return "Function documentation using @doc-formal")) (: @doc (-> Atom DocDescription DocInformal)) (: @doc (-> Atom DocDescription DocParameters DocReturnInformal DocInformal)) + +(@doc @desc + (@desc "Used for documentation purposes. Description of function starts with @desc as a part of @doc") + (@params ( + (@param "String containing function description"))) + (@return "Function description")) (: @desc (-> String DocDescription)) + +(@doc @param + (@desc "Used for documentation purposes. Description of function parameter starts with @param as a part of @params which is a part of @doc") + (@params ( + (@param "String containing parameter description"))) + (@return "Parameter description")) (: @param (-> String DocParameterInformal)) +(: @param (-> DocType DocDescription DocParameter)) + +(@doc @return + (@desc "Used for documentation purposes. Description of function return value starts with @return as a part of @doc") + (@params ( + (@param "String containing return value description"))) + (@return "Return value description")) (: @return (-> String DocReturnInformal)) +(: @return (-> DocType DocDescription DocReturn)) +(@doc @doc-formal + (@desc "Used for documentation purposes. get-doc returns documentation starting with @doc-formal symbol. @doc-formal contains 6 or 4 parameters depending on the entity being described (functions being described using 6 parameters, atoms - 4 parameters)") + (@params ( + (@param "Function/Atom name for which documentation is to be displayed. Format (@item name)") + (@param "Contains (@kind function) or (@kind atom) depends on entity which documentation is displayed") + (@param "Contains type notation of function/atom") + (@param "Function/atom description") + (@param "(Functions only). Description of function parameters") + (@param "(Functions only). Description of function's return value"))) + (@return "Expression containing full documentation on function")) (: @doc-formal (-> DocItem DocKindFunction DocType DocDescription DocParameters DocReturn DocFormal)) (: @doc-formal (-> DocItem DocKindAtom DocType DocDescription DocFormal)) + +(@doc @item + (@desc "Used for documentation purposes. Converts atom/function's name to DocItem") + (@params ( + (@param "Atom/Function name to be documented"))) + (@return "(@item Atom) entity")) (: @item (-> Atom DocItem)) + +; TODO: help! gives two outputs +;Atom (@kind function): (%Undefined% (-> Atom Atom)) Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case +;Atom (@kind function): DocKindFunction Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case +(@doc (@kind function) + (@desc "Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case")) (: (@kind function) DocKindFunction) + +(@doc (@kind atom) + (@desc "Used for documentation purposes. Shows type of entity to be documented. (@kind atom) in this case")) (: (@kind atom) DocKindAtom) + +(@doc @type + (@desc "Used for documentation purposes. Converts atom/function's type to DocType") + (@params ( + (@param "Atom/Function type to be documented"))) + (@return "(@type Type) entity")) (: @type (-> Type DocType)) -(: @params (-> Expression DocParameters)) -(: @param (-> DocType DocDescription DocParameter)) -(: @return (-> DocType DocDescription DocReturn)) -(= (mod-space! top) &self) -(= (get-type-space $space $atom) - (get-type $atom $space)) +(@doc @params + (@desc "Used for function documentation purposes. Contains several @param entities with description of each @param") + (@params ( + (@param "Several (@param ...) entities"))) + (@return "DocParameters containing description of all parameters of function in form of (@params ((@param ...) (@param ...) ...))")) +(: @params (-> Expression DocParameters)) +(@doc get-doc + (@desc "Returns documentation for the given Atom/Function") + (@params ( + (@param "Atom/Function name for which documentation is needed"))) + (@return "Documentation for the given atom/function")) (: get-doc (-> Atom Atom)) (= (get-doc $atom) (let $meta-type (get-metatype $atom) @@ -452,15 +794,25 @@ (Expression (get-doc-atom $atom)) ($_ (get-doc-single-atom $atom)) )))) +(@doc get-doc-single-atom + (@desc "Function used by get-doc to get documentation on either function or atom. It checks if input name is the name of function or atom and calls correspondent function") + (@params ( + (@param "Atom/Function name for which documentation is needed"))) + (@return "Documentation for the given atom/function")) (: get-doc-single-atom (-> Atom Atom)) (= (get-doc-single-atom $atom) (let $top-space (mod-space! top) (let $type (get-type-space $top-space $atom) - (if (or (is-function-type $type) - (is-function $type)) + (if (is-function-type $type) (get-doc-function $atom $type) (get-doc-atom $atom) )))) +(@doc get-doc-function + (@desc "Function used by get-doc-single-atom to get documentation on a function. It returns documentation on a function if it exists or default documentation with no description otherwise") + (@params ( + (@param "Function name for which documentation is needed") + (@param "Type notation for this function"))) + (@return "Documentation for the given function")) (: get-doc-function (-> Atom Type Atom)) (= (get-doc-function $name $type) (let $top-space (mod-space! top) @@ -470,6 +822,11 @@ (@doc-formal (@item $name) (@kind function) (@type $type) $desc (@params $params') $ret'))) (@doc-formal (@item $name) (@kind function) (@type $type) (@desc "No documentation")) ))) +(@doc undefined-doc-function-type + (@desc "Function used by get-doc-single-atom in case of absence of function's type notation") + (@params ( + (@param "List of parameters for the function we want to get documentation for"))) + (@return "List of %Undefined% number of which depends on input list size. So for two parameters function will return (%Undefined% %Undefined% %Undefined%)")) (: undefined-doc-function-type (-> Expression Type)) (= (undefined-doc-function-type $params) (if (== () $params) (%Undefined%) @@ -477,6 +834,13 @@ (let $tail (undefined-doc-function-type $params-tail) (cons-atom %Undefined% $tail) )))) +(@doc get-doc-params + (@desc "Function used by get-doc-function to get function's parameters documentation (including return value)") + (@params ( + (@param "List of parameters in form of ((@param Description) (@param Description)...)") + (@param "Return value's description in form of (@return Description)") + (@param "Type notation without -> starting symbol e.g. (Atom Atom Atom)"))) + (@return "United list of params and return value each augmented with its type. E.g. (((@param (@type Atom) (@desc Description)) (@param (@type Atom) (@desc Description2))) (@return (@type Atom) (@desc Description)))")) (: get-doc-params (-> Expression Atom Expression (Expression Atom))) (= (get-doc-params $params $ret $types) (let $head-type (car-atom $types) @@ -490,6 +854,11 @@ (let $result-params (cons-atom (@param (@type $head-type) (@desc $param-desc)) $params') ($result-params $result-ret) )))))))) +(@doc get-doc-atom + (@desc "Function used by get-doc (in case of input type Expression) and get-doc-single-atom (in case input value is not a function) to get documentation on input value") + (@params ( + (@param "Atom's name to get documentation for"))) + (@return "Documentation on input Atom")) (: get-doc-atom (-> Atom Atom)) (= (get-doc-atom $atom) (let $top-space (mod-space! top) @@ -500,21 +869,11 @@ (get-doc-function $atom %Undefined%) (@doc-formal (@item $atom) (@kind atom) (@type $type) (@desc "No documentation")) ))))) -; TODO: Type is used here, but there is no definition for the -> type -; constructor for instance, thus in practice it matches because -> has -; %Undefined% type. We need to assign proper type to -> and other type -; constructors but it is not possible until we support vararg types. -(: is-function-type (-> Type Bool)) -(= (is-function-type $type) - (let $type-meta (get-metatype $type) - (case $type-meta ( - (Expression - (let $first (car-atom $type) - (if (== $first ->) True False) )) - ($_ False) )))) - - - +(@doc help! + (@desc "Function prints documentation for the input atom.") + (@params ( + (@param "Input to get documentation for"))) + (@return "Unit atom")) (: help! (-> Atom (->))) (= (help! $atom) (case (get-doc $atom) ( @@ -534,11 +893,22 @@ () )) ($other (Error $other "Cannot match @doc-formal structure") )))) +(@doc help-param! + (@desc "Function used by function help! to output parameters using println!") + (@params ( + (@param "Parameters list"))) + (@return "Unit atom")) (: help-param! (-> Atom (->))) (= (help-param! $param) (let (@param (@type $type) (@desc $desc)) $param - (println! (format-args " {} {}" ($type $desc))) )) + (println! (format-args " {} {}" ((type $type) $desc))) )) +(@doc for-each-in-atom + (@desc "Applies function passed as a second argument to each atom inside first argument") + (@params ( + (@param "Expression to each atom in which function will be applied") + (@param "Function to apply"))) + (@return "Unit atom")) (: for-each-in-atom (-> Expression Atom (->))) (= (for-each-in-atom $expr $func) (if (noreduce-eq $expr ()) @@ -548,6 +918,12 @@ (let $_ ($func $head) (for-each-in-atom $tail $func) ))))) +(@doc noreduce-eq + (@desc "Checks equality of two atoms without reducing them") + (@params ( + (@param "First atom") + (@param "Second atom"))) + (@return "True if not reduced atoms are equal, False - otherwise")) (: noreduce-eq (-> Atom Atom Bool)) (= (noreduce-eq $a $b) (== (quote $a) (quote $b))) @@ -561,6 +937,310 @@ (@param "Atomspace to add atom into") (@param "Atom to add"))) (@return "Unit atom")) + + +(@doc new-space + (@desc "Creates new Atomspace which could be used further in the program as a separate from &self Atomspace") + (@params ()) + (@return "Reference to a new space")) + +(@doc remove-atom + (@desc "Removes atom from the input Atomspace") + (@params ( + (@param "Reference to the space from which the Atom needs to be removed") + (@param "Atom to be removed"))) + (@return "Unit atom")) +(@doc get-atoms + (@desc "Shows all atoms in the input Atomspace") + (@params ( + (@param "Reference to the space"))) + (@return "List of all atoms in the input space")) +(@doc new-state + (@desc "Creates a new state atom wrapping its argument") + (@params ( + (@param "Atom to be wrapped"))) + (@return "Returns (State $value) where $value is an argument to a new-state")) + +(@doc change-state! + (@desc "Changes input state's wrapped atom to another value (second argument). E.g. (change-state! (State 5) 6) -> (State 6)") + (@params ( + (@param "State created by new-state function") + (@param "Atom which will replace wrapped atom in the input state"))) + (@return "State with replaced wrapped atom")) + +(@doc get-state + (@desc "Gets a state as an argument and returns its wrapped atom. E.g. (get-state (State 5)) -> 5") + (@params ( + (@param "State"))) + (@return "Atom wrapped by state")) + +(@doc get-type + (@desc "Returns type notation of input atom") + (@params ( + (@param "Atom to get type for"))) + (@return "Type notation or %Undefined% if there is no type for input Atom")) + +(@doc get-type-space + (@desc "Returns type notation of input Atom (second argument) relative to a specified atomspace (first argument)") + (@params ( + (@param "Atomspace where type notation for input atom will be searched") + (@param "Atom to get type for"))) + (@return "Type notation or %Undefined% if there is no type for input Atom in provided atomspace")) + +(@doc get-metatype + (@desc "Returns metatype of the input atom") + (@params ( + (@param "Atom to get metatype for"))) + (@return "Metatype of input atom")) + + +(@doc match + (@desc "Searches for all declared atoms corresponding to the given pattern (second argument) and produces the output pattern (third argument)") + (@params ( + (@param "A grounded atom referencing a Space") + (@param "Pattern atom to be matched") + (@param "Output pattern typically containing variables from the input pattern"))) + (@return "If match was successfull it outputs pattern (third argument) with filled variables (if any were present in pattern) using matched pattern (second argument). Nothing - otherwise")) + + + +(@doc register-module! + (@desc "Takes a file system path (first argument) and loads the module into the runner") + (@params ( + (@param "File system path"))) + (@return "Unit atom")) + +(@doc mod-space! + (@desc "Returns the space of the module (first argument) and tries to load the module if it is not loaded into the module system") + (@params ( + (@param "Module name"))) + (@return "Space name")) + +(@doc print-mods! + (@desc "Prints all modules with their correspondent spaces") + (@params ()) + (@return "Unit atom")) + + +(@doc sealed + (@desc "Replaces all occurrences of any var from var list (first argument) inside atom (second argument) by unique variable. Can be used to create a locally scoped variables") + (@params ( + (@param "Variable list e.g. ($x $y)") + (@param "Atom which uses those variables"))) + (@return "Second argument but with variables being replaced with unique variables")) + +(@doc assertEqual + (@desc "Compares (sets of) results of evaluation of two expressions") + (@params ( + (@param "First expression") + (@param "Second expression"))) + (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + +(@doc assertEqualToResult + (@desc "Same as assertEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") + (@params ( + (@param "First expression (it will be evaluated)") + (@param "Second expression (it won't be evaluated)"))) + (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + +(@doc collapse + (@desc "Converts a nondeterministic result into a tuple") + (@params ( + (@param "Atom which will be evaluated"))) + (@return "Tuple")) + +(@doc capture + (@desc "Wraps an atom and capture the current space") + (@params ( + (@param "Function name which space need to be captured"))) + (@return "Function")) + +(@doc case + (@desc "Subsequently tests multiple pattern-matching conditions (second argument) for the given value (first argument)") + (@params ( + (@param "Atom (it will be evaluated)") + (@param "Tuple of pairs mapping condition patterns to results"))) + (@return "Result of evaluating of Atom bound to met condition")) + + +(@doc superpose + (@desc "Turns a tuple (first argument) into a nondeterministic result") + (@params ( + (@param "Tuple to be converted"))) + (@return "Argument converted to nondeterministic result")) + + + + +(@doc pragma! + (@desc "Changes global key's (first argument) value to a new one (second argument)") + (@params ( + (@param "Key's name") + (@param "New value"))) + (@return "Unit atom")) + +(@doc import! + (@desc "Imports module using its relative path (second argument) and binds it to the token (first argument) which will represent imported atomspace. If first argument is &self then everything will be imported to current atomspace") + (@params ( + (@param "Symbol, which is turned into the token for accessing the imported module") + (@param "Module name"))) + (@return "Unit atom")) + +(@doc include + (@desc "Works just like import! but with &self as a first argument. So everything from input file will be included in the current atomspace and evaluated") + (@params ( + (@param "Name of metta script to import"))) + (@return "Unit atom")) + + + +(@doc bind! + (@desc "Registers a new token which is replaced with an atom during the parsing of the rest of the program") + (@params ( + (@param "Token name") + (@param "Atom, which is associated with the token after reduction"))) + (@return "Unit atom")) + + +(@doc trace! + (@desc "Prints its first argument and returns second. Both arguments will be evaluated before processing") + (@params ( + (@param "Atom to print") + (@param "Atom to return"))) + (@return "Evaluated second input")) + +(@doc println! + (@desc "Prints a line of text to the console") + (@params ( + (@param "Expression/atom to be printed out"))) + (@return "Unit atom")) + +(@doc format-args + (@desc "Fills {} symbols in the input expression with atoms from the second expression. E.g. (format-args (Probability of {} is {}%) (head 50)) gives [(Probability of head is 50%)]. Atoms in the second input value could be variables") + (@params ( + (@param "Expression with {} symbols to be replaced") + (@param "Atoms to be placed inside expression instead of {}"))) + (@return "Expression with replaced {} with atoms")) + +(@doc sealed + (@desc "Replaces all occurrences of any var from var list (first argument) inside atom (second argument) by unique variable. Can be used to create a locally scoped variables") + (@params ( + (@param "Variable list e.g. ($x $y)") + (@param "Atom which uses those variables"))) + (@return "Second argument but with variables being replaced with unique variables")) + + +; TODO: Segmentation fault (core dumped) when calling !(help &self) +; TODO: help! not working for &self (segmentation fault) +;(@doc &self +; (@desc "Returns reference to the current atomspace") +; (@params ()) +; (@return "Reference to the current atomspace")) + +; TODO: help! not working for operations which are defined in both Python and +; Rust standard library: +, -, *, /, %, <, >, <=, >=, == +(@doc + + (@desc "Sums two numbers") + (@params ( + (@param "Addend") + (@param "Augend"))) + (@return "Sum")) + +(@doc - + (@desc "Subtracts second argument from first one") + (@params ( + (@param "Minuend") + (@param "Deductible"))) + (@return "Difference")) + +(@doc * + (@desc "Multiplies two numbers") + (@params ( + (@param "Multiplier") + (@param "Multiplicand"))) + (@return "Product")) + +(@doc / + (@desc "Divides first argument by second one") + (@params ( + (@param "Dividend") + (@param "Divisor"))) + (@return "Fraction")) + +(@doc % + (@desc "Modulo operator. It returns remainder of dividing first argument by second argument") + (@params ( + (@param "Dividend") + (@param "Divisor"))) + (@return "Remainder")) + +(@doc < + (@desc "Less than. Checks if first argument is less than second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is less than second, False - otherwise")) + +(@doc > + (@desc "Greater than. Checks if first argument is greater than second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is greater than second, False - otherwise")) + +(@doc <= + (@desc "Less than or equal. Checks if first argument is less than or equal to second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is less than or equal to second, False - otherwise")) + +(@doc >= + (@desc "Greater than or equal. Checks if first argument is greater than or equal to second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is greater than or equal to second, False - otherwise")) + +(@doc == + (@desc "Checks equality for two arguments of the same type") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Returns True if two arguments are equal, False - otherwise. If arguments are of different type function returns Error currently")) + +(@doc unique + (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") + (@params ( + (@param "Non-deterministic set of values"))) + (@return "Unique values from input set")) + +(@doc union + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Union of sets")) + +(@doc intersection + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Intersection of sets")) + +(@doc subtraction + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Subtraction of sets")) + +(@doc git-module! + (@desc "Provides access to module in a remote git repo, from within MeTTa code. Similar to `register-module!`, this op will bypass the catalog search") + (@params ( + (@param "URL to github repo"))) + (@return "Unit atom")) diff --git a/src/canary/stdlib_minimal.metta b/src/canary/stdlib_minimal.metta index 22124c42328..a732581cebc 100755 --- a/src/canary/stdlib_minimal.metta +++ b/src/canary/stdlib_minimal.metta @@ -1,43 +1,128 @@ -(@doc = +(@doc = (@desc "A symbol used to define reduction rules for expressions.") (@params ( (@param "Pattern to be matched against expression to be reduced") - (@param "Result of reduction or transformation of the first pattern") ) + (@param "Result of reduction or transformation of the first pattern") )) (@return "Not reduced itself unless custom equalities over equalities are added") ) -) (: = (-> $t $t Atom)) (@doc ErrorType (@desc "Type of the atom which contains error")) (: ErrorType Type) + +(@doc Error + (@desc "Error constructor") + (@params ( + (@param "Atom which contains error") + (@param "Error message, can be one of the reserved symbols: BadType, IncorrectNumberOfArguments"))) + (@return "Instance of the error atom")) (: Error (-> Atom Atom ErrorType)) + +(@doc return + (@desc "Returns value from the (function ...) expression") + (@params ( + (@param "Value to be returned"))) + (@return "Passed argument")) (: return (-> $t $t)) +(@doc function + (@desc "Evaluates the argument until it becomes (return ). Then (function (return )) is reduced to the .") + (@params ( + (@param "Atom to be evaluated"))) + (@return "Result of atom's evaluation")) (: function (-> Atom Atom)) + +(@doc eval + (@desc "Evaluates input atom, makes one step of the evaluation") + (@params ( + (@param "Atom to be evaluated, can be reduced via equality expression (= ...) or by calling a grounded function"))) + (@return "Result of evaluation")) (: eval (-> Atom Atom)) + +(@doc chain + (@desc "Evaluates first argument, binds it to the variable (second argument) and then evaluates third argument which contains (or not) mentioned variable") + (@params ( + (@param "Atom to be evaluated") + (@param "Variable") + (@param "Atom which will be evaluated at the end"))) + (@return "Result of evaluating third input argument")) (: chain (-> Atom Variable Atom Atom)) + +(@doc unify + (@desc "Matches two first arguments and returns third argument if they are matched and forth argument otherwise") + (@params ( + (@param "First atom to unify with") + (@param "Second atom to unify with") + (@param "Result if two atoms unified successfully") + (@param "Result otherwise"))) + (@return "Third argument when first two atoms are matched of forth one otherwise")) (: unify (-> Atom Atom Atom Atom Atom)) -(: cons (-> Atom Atom Atom)) -(: decons (-> Atom Atom)) -(: collapse-bind (-> Atom Atom)) -(: superpose-bind (-> Atom Atom)) -; We specialize them but leaving the old defs in case + +(@doc cons-atom + (@desc "Constructs an expression using two arguments") + (@params ( + (@param "Head of an expression") + (@param "Tail of an expression"))) + (@return "New expression consists of two input arguments")) (: cons-atom (-> Atom Expression Expression)) + +(@doc decons-atom + (@desc "Works as a reverse to cons-atom function. It gets Expression as an input and returns it splitted to head and tail, e.g. (decons-atom (Cons X Nil)) -> (Cons (X Nil))") + (@params ( + (@param "Expression"))) + (@return "Deconsed expression")) (: decons-atom (-> Expression Expression)) + +(@doc collapse-bind + (@desc "Evaluates the Atom (first argument) and returns an expression which contains all alternative evaluations in a form (Atom Bindings). Bindings are represented in a form of a grounded atom.") + (@params ( + (@param "Atom to be evaluated"))) + (@return "All alternative evaluations")) (: collapse-bind (-> Atom Expression)) + +(@doc superpose-bind + (@desc "Complement to the collapse-bind. It takes result of collapse-bind (first argument) and returns only result atoms without bindings") + (@params ( + (@param "Expression in form (Atom Binding)"))) + (@return "Non-deterministic list of Atoms")) (: superpose-bind (-> Expression Atom)) + +(@doc metta + (@desc "Run MeTTa interpreter on atom.") + (@params ( + (@param "Atom to be interpreted") + (@param "Type of input atom") + (@param "Atomspace where intepretation should take place"))) + (@return "Result of interpretation")) (: metta (-> Atom Type Grounded Atom)) +(@doc id + (@desc "Returns its argument") + (@params ( + (@param "Input argument"))) + (@return "Input argument")) (: id (-> Atom Atom)) (= (id $x) $x) -(: apply (-> Atom Variable Atom Atom)) -(= (apply $atom $var $templ) - (function (chain (eval (id $atom)) $var (return $templ))) ) - +(@doc atom-subst + (@desc "Substitutes variable passed as a second argument in the third argument by the first argument") + (@params ( + (@param "Value to use for replacement") + (@param "Variable to replace") + (@param "Template to replace variable by the value"))) + (@return "Template with substituted variable")) (: atom-subst (-> Atom Variable Atom Atom)) (= (atom-subst $atom $var $templ) (function (chain (eval (id $atom)) $var (return $templ))) ) +(@doc if-decons-expr + (@desc "Checks if first argument is non empty expression. If so gets tail and head from the first argument and returns forth argument using head and tail values. Returns fifth argument otherwise.") + (@params ( + (@param "Expression to be deconstructed") + (@param "Head variable") + (@param "Tail variable") + (@param "Template to return if first argument is a non-empty expression") + (@param "Default value to return otherwise"))) + (@return "Either template with head and tail replaced by values or default value")) (: if-decons-expr (-> Expression Variable Variable Atom Atom Atom)) (= (if-decons-expr $atom $head $tail $then $else) (function (eval (if-equal $atom () @@ -45,6 +130,13 @@ (chain (decons-atom $atom) $list (unify $list ($head $tail) (return $then) (return $else)) ))))) +(@doc if-error + (@desc "Checks if first argument is an error atom. Returns second argument if so or third argument otherwise.") + (@params ( + (@param "Atom to be checked for the error") + (@param "Value to return if first argument is an error") + (@param "Value to return otherwise"))) + (@return "Second or third argument")) (: if-error (-> Atom Atom Atom Atom)) (= (if-error $atom $then $else) (function (chain (eval (get-metatype $atom)) $meta @@ -57,6 +149,12 @@ (return $else) )))) (return $else) ))))) +(@doc return-on-error + (@desc "Returns first argument if it is Empty or an error. Returns second argument otherwise.") + (@params ( + (@param "Previous evaluation result") + (@param "Atom for further evaluation"))) + (@return "Return previous result if it is an error or Empty or continue evaluation")) (: return-on-error (-> Atom Atom Atom)) (= (return-on-error $atom $then) (function (eval (if-equal $atom Empty (return (return Empty)) @@ -71,12 +169,26 @@ ; (for example user evaluates `!(switch (unify A B ok Empty) ...)` then ; emptiness of the first argument is checked by interpreter and it will ; break execution when `Empty` is returned. +(@doc switch + (@desc "Subsequently tests multiple pattern-matching conditions (second argument) for the given value (first argument)") + (@params ( + (@param "Atom to be matched with patterns") + (@param "Tuple of pairs mapping condition patterns to results"))) + (@return "Result which corresponds to the pattern which is matched with the passed atom first")) + + (: switch (-> %Undefined% Expression Atom)) (= (switch $atom $cases) (function (chain (decons-atom $cases) $list (chain (eval (switch-internal $atom $list)) $res (chain (eval (if-equal $res NotReducible Empty $res)) $x (return $x)) )))) +(@doc switch-internal + (@desc "This function is being called inside switch function to test one of the cases and it calls switch once again if current condition is not met") + (@params ( + (@param "Atom (it will be evaluated)") + (@param "Deconsed tuple of pairs mapping condition patterns to results"))) + (@return "Result of evaluating of Atom bound to met condition")) (= (switch-internal $atom (($pattern $template) $tail)) (function (unify $atom $pattern (return $template) @@ -85,14 +197,52 @@ +; TODO: Type is used here, but there is no definition for the -> type +; constructor for instance, thus in practice it matches because -> has +; %Undefined% type. We need to assign proper type to -> and other type +; constructors but it is not possible until we support vararg types. +(@doc is-function + (@desc "Function checks if input type is a function type") + (@params ( + (@param "Type atom"))) + (@return "True if type is a function type, False - otherwise")) +(: is-function (-> Type Bool)) +(= (is-function $type) + (function (chain (eval (get-metatype $type)) $meta + (eval (switch ($type $meta) ( + (($type Expression) + (eval (if-decons-expr $type $head $_tail + (unify $head -> (return True) (return False)) + (return (Error (is-function $type) "is-function non-empty expression as an argument")) ))) + (($type $meta) (return False)) + )))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; MeTTa interpreter implementation ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - - +(@doc type-cast + (@desc "Casts atom passed as a first argument to the type passed as a second argument using space as a context") + (@params ( + (@param "Atom to be casted") + (@param "Type to cast atom to") + (@param "Context atomspace"))) + (@return "Atom if casting is successful, (Error ... BadType) otherwise")) +(= (type-cast $atom $type $space) + (function (chain (eval (get-metatype $atom)) $meta + (eval (if-equal $type $meta + (return $atom) + (chain (eval (collapse-bind (eval (get-type $atom $space)))) $collapsed + (chain (eval (map-atom $collapsed $pair (eval (first-from-pair $pair)))) $actual-types + (chain (eval (foldl-atom $actual-types False $a $b (eval (match-type-or $a $b $type)))) $is-some-comp + (eval (if $is-some-comp + (return $atom) + (return (Error $atom BadType)) )))))))))) +(@doc match-types + (@desc "Checks if two types can be unified and returns third argument if so, fourth - otherwise") + (@params ( + (@param "First type") + (@param "Second type") + (@param "Atom to be returned if types can be unified") + (@param "Atom to be returned if types cannot be unified"))) + (@return "Third or fourth argument")) (= (match-types $type1 $type2 $then $else) (function (eval (if-equal $type1 %Undefined% (return $then) @@ -104,43 +254,36 @@ (return $then) (unify $type1 $type2 (return $then) (return $else)) )))))))))) +(@doc first-from-pair + (@desc "Gets a pair as a first argument and returns first atom from pair") + (@params ( + (@param "Pair"))) + (@return "First atom from a pair")) (= (first-from-pair $pair) (function (unify $pair ($first $second) (return $first) (return (Error (first-from-pair $pair) "incorrect pair format"))))) +(@doc match-type-or + (@desc "Checks if two types (second and third arguments) can be unified and returns result of OR operation between first argument and type checking result") + (@params ( + (@param "Boolean value") + (@param "First type") + (@param "Second type"))) + (@return "True or False")) (= (match-type-or $folded $next $type) (function (chain (eval (match-types $next $type True False)) $matched (chain (eval (or $folded $matched)) $or (return $or)) ))) -(= (type-cast $atom $type $space) - (function (chain (eval (get-metatype $atom)) $meta - (eval (if-equal $type $meta - (return $atom) - (chain (eval (collapse-bind (eval (get-type $atom $space)))) $collapsed - (chain (eval (map-atom $collapsed $pair (eval (first-from-pair $pair)))) $actual-types - (chain (eval (foldl-atom $actual-types False $a $b (eval (match-type-or $a $b $type)))) $is-some-comp - (eval (if $is-some-comp - (return $atom) - (return (Error $atom BadType)) )))))))))) - -; TODO: Type is used here, but there is no definition for the -> type -; constructor for instance, thus in practice it matches because -> has -; %Undefined% type. We need to assign proper type to -> and other type -; constructors but it is not possible until we support vararg types. -(: is-function (-> Type Bool)) -(= (is-function $type) - (function (chain (eval (get-metatype $type)) $meta - (eval (switch ($type $meta) ( - (($type Expression) - (eval (if-decons-expr $type $head $_tail - (unify $head -> (return True) (return False)) - (return (Error (is-function $type) "is-function non-empty expression as an argument")) ))) - (($type $meta) (return False)) - )))))) - +(@doc filter-atom + (@desc "Function takes list of atoms (first argument), variable (second argument) and filter predicate (third argument) and returns list with items which passed filter. E.g. (filter-atom (1 2 3 4) $v (eval (> $v 2))) will give (3 4)") + (@params ( + (@param "List of atoms") + (@param "Variable") + (@param "Filter predicate"))) + (@return "Filtered list")) (: filter-atom (-> Expression Variable Atom Expression)) (= (filter-atom $list $var $filter) (function (eval (if-decons-expr $list $head $tail @@ -152,6 +295,13 @@ (return $tail-filtered) ))))) (return ()) )))) +(@doc map-atom + (@desc "Function takes list of atoms (first argument), variable to be used inside (second variable) and an expression which will be evaluated for each atom in list (third argument). Expression should contain variable. So e.g. (map-atom (1 2 3 4) $v (eval (+ $v 1))) will give (2 3 4 5)") + (@params ( + (@param "List of atoms") + (@param "Variable name") + (@param "Template using variable"))) + (@return "Result of evaluating template for each atom in a list")) (: map-atom (-> Expression Variable Atom Expression)) (= (map-atom $list $var $map) (function (eval (if-decons-expr $list $head $tail @@ -161,6 +311,15 @@ (chain (cons-atom $head-mapped $tail-mapped) $res (return $res)) ))) (return ()) )))) +(@doc foldl-atom + (@desc "Function takes list of values (first argument), initial value (second argument) and operation (fifth argument) and applies it consequently to the list of values, using init value as a start. It also takes two variables (third and fourth argument) to use them inside") + (@params ( + (@param "List of values") + (@param "Init value") + (@param "Variable") + (@param "Variable") + (@param "Operation"))) + (@return "Result of applying operation to the list of values")) (: foldl-atom (-> Expression Atom Variable Variable Atom Atom)) (= (foldl-atom $list $init $a $b $op) (function (eval (if-decons-expr $list $head $tail @@ -170,115 +329,20 @@ (chain (eval (foldl-atom $tail $head-folded $a $b $op)) $res (return $res)) ))) (return $init) )))) -(: separate-errors (-> Expression Expression Expression)) -(= (separate-errors $succ-err $res) - (function (unify $succ-err ($suc $err) - (unify $res ($a $_b) - (eval (if-error $a - (chain (cons-atom $res $err) $err' (return ($suc $err'))) - (chain (cons-atom $res $suc) $suc' (return ($suc' $err))) )) - (return $succ-err)) - (return $succ-err) ))) - -(= (check-alternatives $atom) - (function - (chain (collapse-bind $atom) $collapsed - ;(chain (eval (print-alternatives! $atom $collapsed)) $_ - (chain (eval (foldl-atom $collapsed (() ()) $succ-err $res - (eval (separate-errors $succ-err $res)))) $separated - (unify $separated ($success $error) - (chain (eval (if-equal $success () $error $success)) $filtered - (chain (superpose-bind $filtered) $ret (return $ret)) ) - (return (Error (check-alternatives $atom) "list of results was not filtered correctly")) ))))) - -(= (interpret $atom $type $space) - (function (chain (eval (get-metatype $atom)) $meta - (eval (if-equal $type Atom - (return $atom) - (eval (if-equal $type $meta - (return $atom) - (eval (switch ($type $meta) ( - (($type Variable) (return $atom)) - (($type Symbol) - (chain (eval (type-cast $atom $type $space)) $ret (return $ret))) - (($type Grounded) - (chain (eval (type-cast $atom $type $space)) $ret (return $ret))) - (($type Expression) - (chain (eval (check-alternatives (eval (interpret-expression $atom $type $space)))) $ret (return $ret))) - )))))))))) - -(= (interpret-expression $atom $type $space) - (function (eval (if-decons-expr $atom $op $args - (chain (eval (get-type $op $space)) $op-type - (chain (eval (is-function $op-type)) $is-func - (unify $is-func True - (chain (eval (interpret-func $atom $op-type $type $space)) $reduced-atom - (chain (eval (metta-call $reduced-atom $type $space)) $ret (return $ret)) ) - (chain (eval (interpret-tuple $atom $space)) $reduced-atom - (chain (eval (metta-call $reduced-atom $type $space)) $ret (return $ret)) )))) - (chain (eval (type-cast $atom $type $space)) $ret (return $ret)) )))) - -(= (interpret-func $expr $type $ret-type $space) - (function (eval (if-decons-expr $expr $op $args - (chain (eval (interpret $op $type $space)) $reduced-op - (eval (return-on-error $reduced-op - (eval (if-decons-expr $type $arrow $arg-types - (chain (eval (interpret-args $expr $args $arg-types $ret-type $space)) $reduced-args - (eval (return-on-error $reduced-args - (chain (cons-atom $reduced-op $reduced-args) $r (return $r))))) - (return (Error $type "Function type expected")) ))))) - (return (Error $expr "Non-empty expression atom is expected")) )))) - -(= (interpret-args $atom $args $arg-types $ret-type $space) - (function (unify $args () - (eval (if-decons-expr $arg-types $actual-ret-type $type-tail - (chain (eval (== () $type-tail)) $correct-type-len - (eval (if $correct-type-len - (eval (match-types $actual-ret-type $ret-type - (return ()) - (return (Error $atom BadType)) )) - (return (Error $atom BadType)) ))) - (return (Error $atom "Too many arguments")) )) - (eval (if-decons-expr $args $head $tail - (eval (if-decons-expr $arg-types $head-type $tail-types - (chain (eval (interpret $head $head-type $space)) $reduced-head - ; check that head was changed otherwise Error or Empty in the head - ; can be just an argument which is passed by intention - (eval (if-equal $reduced-head $head - (chain (eval (interpret-args-tail $atom $reduced-head $tail $tail-types $ret-type $space)) $ret (return $ret)) - (eval (return-on-error $reduced-head - (chain (eval (interpret-args-tail $atom $reduced-head $tail $tail-types $ret-type $space)) $ret (return $ret)) ))))) - (return (Error $atom BadType)) )) - (return (Error (interpret-atom $atom $args $arg-types $space) "Non-empty expression atom is expected")) ))))) - -(= (interpret-args-tail $atom $head $args-tail $args-tail-types $ret-type $space) - (function (chain (eval (interpret-args $atom $args-tail $args-tail-types $ret-type $space)) $reduced-tail - (eval (return-on-error $reduced-tail - (chain (cons-atom $head $reduced-tail) $ret (return $ret)) ))))) - -(= (interpret-tuple $atom $space) - (function (unify $atom () - (return $atom) - (eval (if-decons-expr $atom $head $tail - (chain (eval (interpret $head %Undefined% $space)) $rhead - (eval (if-equal $rhead Empty (return Empty) - (chain (eval (interpret-tuple $tail $space)) $rtail - (eval (if-equal $rtail Empty (return Empty) - (chain (cons-atom $rhead $rtail) $ret (return $ret)) )))))) - (return (Error (interpret-tuple $atom $space) "Non-empty expression atom is expected as an argument")) ))))) - -(= (metta-call $atom $type $space) - (function (eval (if-error $atom (return $atom) - (chain (eval $atom) $result - (eval (if-equal $result NotReducible (return $atom) - (eval (if-equal $result Empty (return Empty) - (eval (if-error $result (return $result) - (chain (eval (interpret $result $type $space)) $ret (return $ret)) ))))))))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Standard library written in MeTTa ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(@doc if-equal + (@desc "Checks if first two arguments are equal and evaluates third argument if equal, fourth argument - otherwise") + (@params ( + (@param "First argument") + (@param "Second argument") + (@param "Atom to be evaluated if arguments are equal") + (@param "Atom to be evaluated if arguments are not equal"))) + (@return "Evaluated third or fourth argument")) + + (@doc if (@desc "Replace itself by one of the arguments depending on condition.") (@params ( @@ -289,94 +353,215 @@ (: if (-> Bool Atom Atom $t)) (= (if True $then $else) $then) (= (if False $then $else) $else) -;`$then`, `$else` should be of `Atom` type to avoid evaluation -; and infinite cycle in inference - - - +; TODO: help! not working for operations which are defined in both Python and +; Rust standard library: or, and, not +(@doc or + (@desc "Logical disjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "True if any of input arguments is True, False - otherwise")) (: or (-> Bool Bool Bool)) (= (or False False) False) (= (or False True) True) (= (or True False) True) (= (or True True) True) +(@doc and + (@desc "Logical conjunction of two arguments") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Returns True if both arguments are True, False - otherwise")) (: and (-> Bool Bool Bool)) (= (and False False) False) (= (and False True) False) (= (and True False) False) (= (and True True) True) +(@doc not + (@desc "Logical negation") + (@params ( + (@param "Argument"))) + (@return "Negates boolean input argument (False -> True, True -> False)")) (: not (-> Bool Bool)) (= (not True) False) (= (not False) True) -(: match (-> Atom Atom Atom %Undefined%)) -;(= (match $space $pattern $template) -; (unify $pattern $space $template Empty)) - -(: let (-> Atom %Undefined% Atom Atom)) +(@doc let + (@desc "Unify two first argument and apply result of the unification on third argument. Second argument is evaluated before unification.") + (@params ( + (@param "First atom to be unified") + (@param "Second atom to be unified") + (@param "Expression which will be evaluated if two first arguments can be unified"))) + (@return "Third argument or Empty")) +(: let (-> Atom %Undefined% Atom %Undefined%)) (= (let $pattern $atom $template) (unify $atom $pattern $template Empty)) -(: let* (-> Expression Atom Atom)) +(@doc let* + (@desc "Same as let but inputs list of pairs of atoms to be unified. For example (let* (($v1 (+ 1 2)) ($v2 (* 5 6))) (+ $v1 $v2))") + (@params ( + (@param "List of pairs, atoms in each pair to be unified") + (@param "Expression which will be evaluated if each pair can be unified"))) + (@return "Second argument or Empty")) +(: let* (-> Expression Atom %Undefined%)) (= (let* $pairs $template) (eval (if-decons-expr $pairs ($pattern $atom) $tail (let $pattern $atom (let* $tail $template)) $template ))) - +(@doc add-reduct + (@desc "Reduces atom (second argument) and adds it into the atomspace (first argument)") + (@params ( + (@param "Atomspace to add atom into") + (@param "Atom to add"))) + (@return "Unit atom")) (: add-reduct (-> Grounded %Undefined% (->))) -(= (add-reduct $dst $atom) (add-atom $dst $atom)) +(= (add-reduct $dst $atom) (add-atom $dst $atom)) +(@doc car-atom + (@desc "Extracts the first atom of an expression as a tuple") + (@params ( + (@param "Expression"))) + (@return "First atom of an expression")) (: car-atom (-> Expression Atom)) (= (car-atom $atom) (eval (if-decons-expr $atom $head $_ $head (Error (car-atom $atom) "car-atom expects a non-empty expression as an argument") ))) +(@doc cdr-atom + (@desc "Extracts the tail of an expression (all except first atom)") + (@params ( + (@param "Expression"))) + (@return "Tail of an expression")) (: cdr-atom (-> Expression Expression)) (= (cdr-atom $atom) (eval (if-decons-expr $atom $_ $tail $tail (Error (cdr-atom $atom) "cdr-atom expects a non-empty expression as an argument") ))) +(@doc quote + (@desc "Prevents atom from being reduced") + (@params ( + (@param "Atom"))) + (@return "Quoted atom")) (: quote (-> Atom Atom)) (= (quote $atom) NotReducible) +(@doc unquote + (@desc "Unquotes quoted atom, e.g. (unquote (quote $x)) returns $x") + (@params ( + (@param "Quoted atom"))) + (@return "Unquoted atom")) (: unquote (-> %Undefined% %Undefined%)) (= (unquote (quote $atom)) $atom) ; TODO: there is no way to define operation which consumes any number of ; arguments and returns unit +(@doc nop + (@desc "Outputs unit atom") + (@params ()) + (@return "Unit atom")) (= (nop) ()) (= (nop $x) ()) - ; TODO: MINIMAL added for compatibility, remove after migration +(@doc empty + (@desc "Cuts evaluation of the non-deterministic branch and removes it from the result") + (@params ()) + (@return "Nothing")) (= (empty) Empty) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Documentation formatting functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - +(@doc @doc + (@desc "Used for documentation purposes. Function documentation starts with @doc") + (@params ( + (@param "Function name") + (@param "Function description. Starts with @desc") + (@param "(Optional) parameters description starting with @params which should contain one or more @param symbols") + (@param "(Optional) description of what function will return. Starts with @return"))) + (@return "Function documentation using @doc-formal")) (: @doc (-> Atom DocDescription DocInformal)) (: @doc (-> Atom DocDescription DocParameters DocReturnInformal DocInformal)) + +(@doc @desc + (@desc "Used for documentation purposes. Description of function starts with @desc as a part of @doc") + (@params ( + (@param "String containing function description"))) + (@return "Function description")) (: @desc (-> String DocDescription)) + +(@doc @param + (@desc "Used for documentation purposes. Description of function parameter starts with @param as a part of @params which is a part of @doc") + (@params ( + (@param "String containing parameter description"))) + (@return "Parameter description")) (: @param (-> String DocParameterInformal)) +(: @param (-> DocType DocDescription DocParameter)) + +(@doc @return + (@desc "Used for documentation purposes. Description of function return value starts with @return as a part of @doc") + (@params ( + (@param "String containing return value description"))) + (@return "Return value description")) (: @return (-> String DocReturnInformal)) +(: @return (-> DocType DocDescription DocReturn)) +(@doc @doc-formal + (@desc "Used for documentation purposes. get-doc returns documentation starting with @doc-formal symbol. @doc-formal contains 6 or 4 parameters depending on the entity being described (functions being described using 6 parameters, atoms - 4 parameters)") + (@params ( + (@param "Function/Atom name for which documentation is to be displayed. Format (@item name)") + (@param "Contains (@kind function) or (@kind atom) depends on entity which documentation is displayed") + (@param "Contains type notation of function/atom") + (@param "Function/atom description") + (@param "(Functions only). Description of function parameters") + (@param "(Functions only). Description of function's return value"))) + (@return "Expression containing full documentation on function")) (: @doc-formal (-> DocItem DocKindFunction DocType DocDescription DocParameters DocReturn DocFormal)) (: @doc-formal (-> DocItem DocKindAtom DocType DocDescription DocFormal)) + +(@doc @item + (@desc "Used for documentation purposes. Converts atom/function's name to DocItem") + (@params ( + (@param "Atom/Function name to be documented"))) + (@return "(@item Atom) entity")) (: @item (-> Atom DocItem)) + +; TODO: help! gives two outputs +;Atom (@kind function): (%Undefined% (-> Atom Atom)) Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case +;Atom (@kind function): DocKindFunction Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case +(@doc (@kind function) + (@desc "Used for documentation purposes. Shows type of entity to be documented. (@kind function) in this case")) (: (@kind function) DocKindFunction) + +(@doc (@kind atom) + (@desc "Used for documentation purposes. Shows type of entity to be documented. (@kind atom) in this case")) (: (@kind atom) DocKindAtom) + +(@doc @type + (@desc "Used for documentation purposes. Converts atom/function's type to DocType") + (@params ( + (@param "Atom/Function type to be documented"))) + (@return "(@type Type) entity")) (: @type (-> Type DocType)) -(: @params (-> Expression DocParameters)) -(: @param (-> DocType DocDescription DocParameter)) -(: @return (-> DocType DocDescription DocReturn)) +(@doc @params + (@desc "Used for function documentation purposes. Contains several @param entities with description of each @param") + (@params ( + (@param "Several (@param ...) entities"))) + (@return "DocParameters containing description of all parameters of function in form of (@params ((@param ...) (@param ...) ...))")) +(: @params (-> Expression DocParameters)) +(@doc get-doc + (@desc "Returns documentation for the given Atom/Function") + (@params ( + (@param "Atom/Function name for which documentation is needed"))) + (@return "Documentation for the given atom/function")) (: get-doc (-> Atom Atom)) (= (get-doc $atom) (let $meta-type (get-metatype $atom) @@ -384,6 +569,11 @@ (Expression (get-doc-atom $atom)) ($_ (get-doc-single-atom $atom)) )))) +(@doc get-doc-single-atom + (@desc "Function used by get-doc to get documentation on either function or atom. It checks if input name is the name of function or atom and calls correspondent function") + (@params ( + (@param "Atom/Function name for which documentation is needed"))) + (@return "Documentation for the given atom/function")) (: get-doc-single-atom (-> Atom Atom)) (= (get-doc-single-atom $atom) (let $top-space (mod-space! top) @@ -392,6 +582,12 @@ (get-doc-function $atom $type) (get-doc-atom $atom) )))) +(@doc get-doc-function + (@desc "Function used by get-doc-single-atom to get documentation on a function. It returns documentation on a function if it exists or default documentation with no description otherwise") + (@params ( + (@param "Function name for which documentation is needed") + (@param "Type notation for this function"))) + (@return "Documentation for the given function")) (: get-doc-function (-> Atom Type Atom)) (= (get-doc-function $name $type) (let $top-space (mod-space! top) @@ -401,6 +597,11 @@ (@doc-formal (@item $name) (@kind function) (@type $type) $desc (@params $params') $ret'))) (@doc-formal (@item $name) (@kind function) (@type $type) (@desc "No documentation")) ))) +(@doc undefined-doc-function-type + (@desc "Function used by get-doc-single-atom in case of absence of function's type notation") + (@params ( + (@param "List of parameters for the function we want to get documentation for"))) + (@return "List of %Undefined% number of which depends on input list size. So for two parameters function will return (%Undefined% %Undefined% %Undefined%)")) (: undefined-doc-function-type (-> Expression Type)) (= (undefined-doc-function-type $params) (if (== () $params) (%Undefined%) @@ -408,6 +609,13 @@ (let $tail (undefined-doc-function-type $params-tail) (cons-atom %Undefined% $tail) )))) +(@doc get-doc-params + (@desc "Function used by get-doc-function to get function's parameters documentation (including return value)") + (@params ( + (@param "List of parameters in form of ((@param Description) (@param Description)...)") + (@param "Return value's description in form of (@return Description)") + (@param "Type notation without -> starting symbol e.g. (Atom Atom Atom)"))) + (@return "United list of params and return value each augmented with its type. E.g. (((@param (@type Atom) (@desc Description)) (@param (@type Atom) (@desc Description2))) (@return (@type Atom) (@desc Description)))")) (: get-doc-params (-> Expression Atom Expression (Expression Atom))) (= (get-doc-params $params $ret $types) (let $head-type (car-atom $types) @@ -421,6 +629,11 @@ (let $result-params (cons-atom (@param (@type $head-type) (@desc $param-desc)) $params') ($result-params $result-ret) )))))))) +(@doc get-doc-atom + (@desc "Function used by get-doc (in case of input type Expression) and get-doc-single-atom (in case input value is not a function) to get documentation on input value") + (@params ( + (@param "Atom's name to get documentation for"))) + (@return "Documentation on input Atom")) (: get-doc-atom (-> Atom Atom)) (= (get-doc-atom $atom) (let $top-space (mod-space! top) @@ -431,6 +644,11 @@ (get-doc-function $atom %Undefined%) (@doc-formal (@item $atom) (@kind atom) (@type $type) (@desc "No documentation")) ))))) +(@doc help! + (@desc "Function prints documentation for the input atom.") + (@params ( + (@param "Input to get documentation for"))) + (@return "Unit atom")) (: help! (-> Atom (->))) (= (help! $atom) (case (get-doc $atom) ( @@ -450,11 +668,22 @@ () )) ($other (Error $other "Cannot match @doc-formal structure") )))) +(@doc help-param! + (@desc "Function used by function help! to output parameters using println!") + (@params ( + (@param "Parameters list"))) + (@return "Unit atom")) (: help-param! (-> Atom (->))) (= (help-param! $param) (let (@param (@type $type) (@desc $desc)) $param - (println! (format-args " {} {}" ($type $desc))) )) + (println! (format-args " {} {}" ((type $type) $desc))) )) +(@doc for-each-in-atom + (@desc "Applies function passed as a second argument to each atom inside first argument") + (@params ( + (@param "Expression to each atom in which function will be applied") + (@param "Function to apply"))) + (@return "Unit atom")) (: for-each-in-atom (-> Expression Atom (->))) (= (for-each-in-atom $expr $func) (if (noreduce-eq $expr ()) @@ -464,6 +693,12 @@ (let $_ ($func $head) (for-each-in-atom $tail $func) ))))) +(@doc noreduce-eq + (@desc "Checks equality of two atoms without reducing them") + (@params ( + (@param "First atom") + (@param "Second atom"))) + (@return "True if not reduced atoms are equal, False - otherwise")) (: noreduce-eq (-> Atom Atom Bool)) (= (noreduce-eq $a $b) (== (quote $a) (quote $b))) @@ -477,3 +712,290 @@ (@param "Atomspace to add atom into") (@param "Atom to add"))) (@return "Unit atom")) + +(@doc new-space + (@desc "Creates new Atomspace which could be used further in the program as a separate from &self Atomspace") + (@params ()) + (@return "Reference to a new space")) + +(@doc remove-atom + (@desc "Removes atom from the input Atomspace") + (@params ( + (@param "Reference to the space from which the Atom needs to be removed") + (@param "Atom to be removed"))) + (@return "Unit atom")) + +(@doc get-atoms + (@desc "Shows all atoms in the input Atomspace") + (@params ( + (@param "Reference to the space"))) + (@return "List of all atoms in the input space")) + +(@doc new-state + (@desc "Creates a new state atom wrapping its argument") + (@params ( + (@param "Atom to be wrapped"))) + (@return "Returns (State $value) where $value is an argument to a new-state")) + +(@doc change-state! + (@desc "Changes input state's wrapped atom to another value (second argument). E.g. (change-state! (State 5) 6) -> (State 6)") + (@params ( + (@param "State created by new-state function") + (@param "Atom which will replace wrapped atom in the input state"))) + (@return "State with replaced wrapped atom")) + +(@doc get-state + (@desc "Gets a state as an argument and returns its wrapped atom. E.g. (get-state (State 5)) -> 5") + (@params ( + (@param "State"))) + (@return "Atom wrapped by state")) + +(@doc get-type + (@desc "Returns type notation of input atom") + (@params ( + (@param "Atom to get type for"))) + (@return "Type notation or %Undefined% if there is no type for input Atom")) + +(@doc get-type-space + (@desc "Returns type notation of input Atom (second argument) relative to a specified atomspace (first argument)") + (@params ( + (@param "Atomspace where type notation for input atom will be searched") + (@param "Atom to get type for"))) + (@return "Type notation or %Undefined% if there is no type for input Atom in provided atomspace")) + +(@doc get-metatype + (@desc "Returns metatype of the input atom") + (@params ( + (@param "Atom to get metatype for"))) + (@return "Metatype of input atom")) + +(@doc match + (@desc "Searches for all declared atoms corresponding to the given pattern (second argument) inside space (first argument) and returns the output template (third argument)") + (@params ( + (@param "Atomspace to search pattern") + (@param "Pattern atom to be searched") + (@param "Output template typically containing variables from the input pattern"))) + (@return "If match was successfull it outputs template (third argument) with filled variables (if any were present in pattern) using matched pattern (second argument). Empty - otherwise")) + +(@doc register-module! + (@desc "Takes a file system path (first argument) and loads the module into the runner") + (@params ( + (@param "File system path"))) + (@return "Unit atom")) + +(@doc mod-space! + (@desc "Returns the space of the module (first argument) and tries to load the module if it is not loaded into the module system") + (@params ( + (@param "Module name"))) + (@return "Space name")) + +(@doc print-mods! + (@desc "Prints all modules with their correspondent spaces") + (@params ()) + (@return "Unit atom")) + +(@doc assertEqual + (@desc "Compares (sets of) results of evaluation of two expressions") + (@params ( + (@param "First expression") + (@param "Second expression"))) + (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + +(@doc assertEqualToResult + (@desc "Same as assertEqual but it doesn't evaluate second argument. Second argument is considered as a set of values of the first argument's evaluation") + (@params ( + (@param "First expression (it will be evaluated)") + (@param "Second expression (it won't be evaluated)"))) + (@return "Unit atom if both expression after evaluation is equal, error - otherwise")) + +(@doc collapse + (@desc "Converts a nondeterministic result into a tuple") + (@params ( + (@param "Atom which will be evaluated"))) + (@return "Tuple")) + +(@doc capture + (@desc "Wraps an atom and capture the current space") + (@params ( + (@param "Function name which space need to be captured"))) + (@return "Function")) + +(@doc case + (@desc "Subsequently tests multiple pattern-matching conditions (second argument) for the given value (first argument)") + (@params ( + (@param "Atom (it will be evaluated)") + (@param "Tuple of pairs mapping condition patterns to results"))) + (@return "Result of evaluating of Atom bound to met condition")) + + +(@doc superpose + (@desc "Turns a tuple (first argument) into a nondeterministic result") + (@params ( + (@param "Tuple to be converted"))) + (@return "Argument converted to nondeterministic result")) + + +(@doc pragma! + (@desc "Changes global key's (first argument) value to a new one (second argument)") + (@params ( + (@param "Key's name") + (@param "New value"))) + (@return "Unit atom")) + +(@doc import! + (@desc "Imports module using its relative path (second argument) and binds it to the token (first argument) which will represent imported atomspace. If first argument is &self then everything will be imported to current atomspace") + (@params ( + (@param "Symbol, which is turned into the token for accessing the imported module") + (@param "Module name"))) + (@return "Unit atom")) + +(@doc include + (@desc "Works just like import! but with &self as a first argument. So everything from input file will be included in the current atomspace and evaluated") + (@params ( + (@param "Name of metta script to import"))) + (@return "Unit atom")) + +(@doc bind! + (@desc "Registers a new token which is replaced with an atom during the parsing of the rest of the program") + (@params ( + (@param "Token name") + (@param "Atom, which is associated with the token after reduction"))) + (@return "Unit atom")) + +(@doc trace! + (@desc "Prints its first argument and returns second. Both arguments will be evaluated before processing") + (@params ( + (@param "Atom to print") + (@param "Atom to return"))) + (@return "Evaluated second input")) + +(@doc println! + (@desc "Prints a line of text to the console") + (@params ( + (@param "Expression/atom to be printed out"))) + (@return "Unit atom")) + +(@doc format-args + (@desc "Fills {} symbols in the input expression with atoms from the second expression. E.g. (format-args (Probability of {} is {}%) (head 50)) gives [(Probability of head is 50%)]. Atoms in the second input value could be variables") + (@params ( + (@param "Expression with {} symbols to be replaced") + (@param "Atoms to be placed inside expression instead of {}"))) + (@return "Expression with replaced {} with atoms")) + +(@doc sealed + (@desc "Replaces all occurrences of any var from var list (first argument) inside atom (second argument) by unique variable. Can be used to create a locally scoped variables") + (@params ( + (@param "Variable list e.g. ($x $y)") + (@param "Atom which uses those variables"))) + (@return "Second argument but with variables being replaced with unique variables")) + +; TODO: help! not working for &self (segmentation fault) +;(@doc &self +; (@desc "Returns reference to the current atomspace") +; (@params ()) +; (@return "Reference to the current atomspace")) + +; TODO: help! not working for operations which are defined in both Python and +; Rust standard library: +, -, *, /, %, <, >, <=, >=, == +(@doc + + (@desc "Sums two numbers") + (@params ( + (@param "Addend") + (@param "Augend"))) + (@return "Sum")) + +(@doc - + (@desc "Subtracts second argument from first one") + (@params ( + (@param "Minuend") + (@param "Deductible"))) + (@return "Difference")) + +(@doc * + (@desc "Multiplies two numbers") + (@params ( + (@param "Multiplier") + (@param "Multiplicand"))) + (@return "Product")) + +(@doc / + (@desc "Divides first argument by second one") + (@params ( + (@param "Dividend") + (@param "Divisor"))) + (@return "Fraction")) + +(@doc % + (@desc "Modulo operator. It returns remainder of dividing first argument by second argument") + (@params ( + (@param "Dividend") + (@param "Divisor"))) + (@return "Remainder")) + +(@doc < + (@desc "Less than. Checks if first argument is less than second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is less than second, False - otherwise")) + +(@doc > + (@desc "Greater than. Checks if first argument is greater than second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is greater than second, False - otherwise")) + +(@doc <= + (@desc "Less than or equal. Checks if first argument is less than or equal to second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is less than or equal to second, False - otherwise")) + +(@doc >= + (@desc "Greater than or equal. Checks if first argument is greater than or equal to second one") + (@params ( + (@param "First number") + (@param "Second number"))) + (@return "True if first argument is greater than or equal to second, False - otherwise")) + +(@doc == + (@desc "Checks equality for two arguments of the same type") + (@params ( + (@param "First argument") + (@param "Second argument"))) + (@return "Returns True if two arguments are equal, False - otherwise. If arguments are of different type function returns Error currently")) + +(@doc unique + (@desc "Function takes non-deterministic input (first argument) and returns only unique entities. E.g. (unique (superpose (a b c d d))) -> [a, b, c, d]") + (@params ( + (@param "Non-deterministic set of values"))) + (@return "Unique values from input set")) + +(@doc union + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their union. E.g. (union (superpose (a b b c)) (superpose (b c c d))) -> [a, b, b, c, b, c, c, d]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Union of sets")) + +(@doc intersection + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their intersection. E.g. (intersection (superpose (a b c c)) (superpose (b c c c d))) -> [b, c, c]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Intersection of sets")) + +(@doc subtraction + (@desc "Function takes two non-deterministic inputs (first and second argument) and returns their subtraction. E.g. !(subtraction (superpose (a b b c)) (superpose (b c c d))) -> [a, b]") + (@params ( + (@param "Non-deterministic set of values") + (@param "Another non-deterministic set of values"))) + (@return "Subtraction of sets")) + +(@doc git-module! + (@desc "Provides access to module in a remote git repo, from within MeTTa code. Similar to `register-module!`, this op will bypass the catalog search") + (@params ( + (@param "URL to github repo"))) + (@return "Unit atom")) diff --git a/src/canary/stdlib_mettalog_test.metta b/tests/more-anti-regression/stdlib-mettalog/stdlib_mettalog_test.metta old mode 100644 new mode 100755 similarity index 100% rename from src/canary/stdlib_mettalog_test.metta rename to tests/more-anti-regression/stdlib-mettalog/stdlib_mettalog_test.metta From 593be0ac6b24647e8c3f2ace8d59365929ef0c42 Mon Sep 17 00:00:00 2001 From: TeamSPoon Date: Tue, 13 Aug 2024 19:59:30 -0700 Subject: [PATCH 2/6] checkbox display --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index 2c90f310e8a..6c5cdaaef8d 100755 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ mettalog tests/baseline_compat/hyperon-experimental_scripts/b0_chaining_prelim.m + ** Launch Jupyter notebook: (in progress) ** - Contains a Jupyter Kernel for MeTTa (allows runing of MeTTa scripts remotely) ``` @@ -291,3 +292,13 @@ clear ; mettalog --test --v=./src/canary --log --html --compile=false tests/base ``` + +# Metta Functions Task List + +| Function Name | Doc. (@doc) | Test Created | Impl. in Interpreter | Impl. in Transpiler | Arg Types Declared | +|----------------|-------------|--------------|----------------------|---------------------|--------------------| +| `functionA` | - [ ] | - [ ] | - [ ] | - [ ] | - [ ] | +| `functionB` | - [ ] | - [ ] | - [ ] | - [ ] | - [ ] | +| `functionC` | - [ ] | - [ ] | - [ ] | - [ ] | - [ ] | + + From bd84101ccf1816e837ced9d30a83f9d164d18d67 Mon Sep 17 00:00:00 2001 From: TeamSPoon Date: Tue, 13 Aug 2024 22:48:42 -0700 Subject: [PATCH 3/6] initial JUnit.xml generation --- mettalog | 1 + scripts/into_junit.py | 73 +++++++++++++++++++++++++++++++++++++ scripts/test_in_metta.sh | 25 ++++++++++--- src/canary/metta_testing.pl | 14 ++++--- 4 files changed, 102 insertions(+), 11 deletions(-) create mode 100644 scripts/into_junit.py diff --git a/mettalog b/mettalog index 7a018f7994e..71a45621eff 100755 --- a/mettalog +++ b/mettalog @@ -954,6 +954,7 @@ if [[ "$html_flag" == "enable" ]]; then if [ ! -z "$OUTPUT_DIR" ] ;then HTML_OUT="${OUTPUT_DIR}/${HTML_OUT}" fi + export HTML_FILE="${HTML_OUT}" fi export TYPESCRIPT=1 diff --git a/scripts/into_junit.py b/scripts/into_junit.py new file mode 100644 index 00000000000..875e9c15f45 --- /dev/null +++ b/scripts/into_junit.py @@ -0,0 +1,73 @@ +import xml.etree.ElementTree as ET +import sys +import re + +def create_testcase_element(testclass, testname, stdout, identifier, got, expected, status, url): + testcase = ET.Element("testcase", classname=testclass, name=testname) + + description = f"Test {identifier} with URL: {url}" + + if status == "PASS": + system_out = ET.SubElement(testcase, "system-out") + system_out.text = f"Test Report\n\nAssertion: {stdout}\nExpected: {expected}\nActual: {got}\n]]>" + else: # status == "FAIL" + failure_message = f"Test failed: Expected '{expected}' but got '{got}'" + failure = ET.SubElement(testcase, "failure", message=failure_message, type="AssertionError") + failure.text = f"" + system_out = ET.SubElement(testcase, "system-out") + system_out.text = f"Test Report\n\nAssertion: {stdout}\nExpected: {expected}\nActual: {got}\n]]>" + + return testcase + +def parse_test_line(line): + parts = re.split(r'\s*\|\s*(?![^()]*\))', line.strip()) + if len(parts) < 7: + raise ValueError(f"Line does not have the expected number of parts: {len(parts)} found") + + full_identifier = parts[1].strip() # The second field contains the test class and name + status = parts[2].strip() # The third field contains the pass/fail status + url = re.search(r'\((.*?)\)', parts[3]).group(1).strip() # Extract the URL inside the parentheses + stdout = parts[4].strip() # The fifth field contains the assertion + got = parts[5].strip() + expected = parts[6].strip() + + try: + testclass, testname = full_identifier.rsplit('.', 1) + if '.' in testclass: + testname = f"{testclass.split('.')[-1]}.{testname}" # Combine to form the correct testname + if not testclass or not testname: + raise ValueError("Test class or test name is empty after splitting.") + except ValueError as e: + raise ValueError(f"Identifier does not contain the expected format: {full_identifier}. Error: {str(e)}") + + return testclass, testname, stdout, full_identifier, got, expected, status, url + +def generate_junit_xml(input_file): + testsuites = ET.Element("testsuites") + testsuite = ET.Element("testsuite", name="Metta Tests") + testsuites.append(testsuite) + + with open(input_file, 'r') as file: + for line in file: + parts = None + if line.startswith("|"): + try: + parts = re.split(r'\s*\|\s*(?![^()]*\))', line.strip()) + testclass, testname, stdout, full_identifier, got, expected, status, url = parse_test_line(line) + testcase = create_testcase_element(testclass, testname, stdout, full_identifier, got, expected, status, url) + testsuite.append(testcase) + print(f"Processing {testclass}.{testname}: {status}", file=sys.stderr) + except ValueError as e: + print(f"Skipping line due to error: {e}\nLine: {line}\nParts: {parts}", file=sys.stderr) + + tree = ET.ElementTree(testsuites) + return ET.tostring(testsuites, encoding="utf-8", xml_declaration=True).decode("utf-8") + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python generate_junit.py ") + sys.exit(1) + + input_file = sys.argv[1] + junit_xml = generate_junit_xml(input_file) + print(junit_xml) diff --git a/scripts/test_in_metta.sh b/scripts/test_in_metta.sh index a82b301a768..8c58d7015cd 100755 --- a/scripts/test_in_metta.sh +++ b/scripts/test_in_metta.sh @@ -1,6 +1,7 @@ #!/bin/bash SHOULD_EXIT=0 +SHARED_UNITS=/tmp/SHARED.UNITS DEBUG_WHY() { DEBUG "${GREEN}WHY: ${BOLD}${*}${NC}" @@ -430,10 +431,16 @@ generate_final_MeTTaLog() { # Change to the script directory cd "$METTALOG_DIR" || exit 1 + python3 ./scripts/into_junit.py "${SHARED_UNITS}" > "$METTALOG_OUTPUT/junit.xml" + + junit2html "$METTALOG_OUTPUT/junit.xml" + junit2html "$METTALOG_OUTPUT/junit.xml" --summary-matrix + echo "saved to $METTALOG_OUTPUT/junit.xml.html" + # Calculate the number of passed and failed tests - passed=$(grep -c "| PASS |" /tmp/SHARED.UNITS) - failed=$(grep -c "| FAIL |" /tmp/SHARED.UNITS) + passed=$(grep -c "| PASS |" "${SHARED_UNITS}") + failed=$(grep -c "| FAIL |" "${SHARED_UNITS}") total=$((passed + failed)) # Check if total is zero to avoid divide by zero error @@ -443,10 +450,11 @@ generate_final_MeTTaLog() { percent_passed=$(awk -v passed="$passed" -v total="$total" 'BEGIN { printf "%.2f", (passed/total)*100 }') fi + # Create a markdown file with test links and headers - { echo "| STATUS | TEST NAME | TEST CONDITION | ACTUAL RESULT | EXPECTED RESULT |" - echo "|--------|-----------|----------------|---------------|-----------------|" - cat /tmp/SHARED.UNITS | awk -F'\\(|\\) \\| \\(' '{ print $2 " " $0 }' | sort | cut -d' ' -f2- | tac | awk '!seen[$0]++' | tac + { echo "| TEST NAME | STATUS | URL LOCATION | TEST CONDITION | ACTUAL RESULT | EXPECTED RESULT |" + echo "|-----------|--------|--------------|----------------|---------------|-----------------|" + cat "${SHARED_UNITS}" | awk -F'\\(|\\) \\| \\(' '{ print $1 " " $0 }' | sort | cut -d' ' -f2- | tac | awk '!seen[$0]++' | tac } > ./$METTALOG_OUTPUT/PASS_FAIL.md @@ -632,7 +640,7 @@ fi # Delete HTML files if the clean flag is set if [ $clean -eq 1 ]; then delete_html_files - cat /dev/null > /tmp/SHARED.UNITS + cat /dev/null > "${SHARED_UNITS}" fi # Prompt user to rerun all tests if run_tests_auto_reply is not set @@ -652,6 +660,11 @@ fi INTERP_SRC_DIR="$(realpath "${INTERP_SRC_DIR}")" DEBUG "INTERP_SRC_DIR=$INTERP_SRC_DIR" +DEBUG "METTALOG_OUTPUT=$METTALOG_OUTPUT" + +if [[ ! -f "${METTALOG_OUTPUT}/src/" ]]; then + cat /dev/null > "${SHARED_UNITS}" +fi mkdir -p "${METTALOG_OUTPUT}/src/" cp -af "${INTERP_SRC_DIR}/"* "${METTALOG_OUTPUT}/src/" diff --git a/src/canary/metta_testing.pl b/src/canary/metta_testing.pl index 0d69486e840..fc950bdb5ae 100755 --- a/src/canary/metta_testing.pl +++ b/src/canary/metta_testing.pl @@ -130,13 +130,17 @@ (%atom_concat(TEE_FILE,'.UNITS',UNITS), UNITS = '/tmp/SHARED.UNITS', open(UNITS, append, Stream,[encoding(utf8)]), - format(Stream,'| ~w | [~w](https://logicmoo.org/public/metta/reports/~w.metta.html#~w) | ~@ | ~@ | ~@ |~n', - [PASS_FAIL,TestName,Base,TestName,trim_gstring(with_indents(false,write_src([P,C])),200), - trim_gstring(with_indents(false,write_src(G1)),100),with_indents(false,write_src(G2))]),!, + once(getenv('HTML_FILE',HTML_OUT);sformat(HTML_OUT,'~w.metta.html',[Base])), + format(Stream,'| ~w | ~w |[~w](https://logicmoo.org/public/metta/reports/~w#~w) | ~@ | ~@ | ~@ |~n', + [TestName,PASS_FAIL,TestName,HTML_OUT,TestName, + trim_gstring_bar_I(write_src_woi([P,C]),200), + trim_gstring_bar_I(write_src_woi(G1),100), + trim_gstring_bar_I(write_src_woi(G2),100)]),!, close(Stream))). -trim_gstring(Goal, MaxLen) :- - wots(String,Goal), +trim_gstring_bar_I(Goal, MaxLen) :- + wots(String0,Goal), + string_replace(String0,'|','I',String), atom_length(String, Len), ( Len =< MaxLen -> Trimmed = String From 3e469379506674e7c32dd24fbe343ceaa32feb6d Mon Sep 17 00:00:00 2001 From: TeamSPoon Date: Tue, 13 Aug 2024 22:53:17 -0700 Subject: [PATCH 4/6] pip install junit2html into README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6c5cdaaef8d..173f851e2dd 100755 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ cd metta-wam . scripts/ensure_venv # ensures we are runing in a python venv pip install ansi2html # needed for running tests pip install hyperon # needed for running tests +pip install junit2html # needed for test report generation chmod +x INSTALL.sh # Make sure the script is executable . ./INSTALL.sh # Follow the default prompts From 56e0c30645f38f1cc00d3f3333e4620fb5d69620 Mon Sep 17 00:00:00 2001 From: TeamSPoon Date: Wed, 14 Aug 2024 00:03:37 -0700 Subject: [PATCH 5/6] add a test description JUnit.xml generation --- scripts/into_junit.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/into_junit.py b/scripts/into_junit.py index 875e9c15f45..c8eb6a57725 100644 --- a/scripts/into_junit.py +++ b/scripts/into_junit.py @@ -5,7 +5,10 @@ def create_testcase_element(testclass, testname, stdout, identifier, got, expected, status, url): testcase = ET.Element("testcase", classname=testclass, name=testname) - description = f"Test {identifier} with URL: {url}" + # Add properties with test description + properties = ET.SubElement(testcase, "properties") + property_element = ET.SubElement(properties, "property", name="test_description") + property_element.text = stdout if status == "PASS": system_out = ET.SubElement(testcase, "system-out") From 6b5a2d96b18a1ae36cffc0c1a03a5dd67accd580 Mon Sep 17 00:00:00 2001 From: TeamSPoon Date: Wed, 14 Aug 2024 00:32:27 -0700 Subject: [PATCH 6/6] Fake scripts/run_commit_tests.sh --- .github/workflows/ci.yml | 46 +++++++++++++++++++++++++++++++++++++ scripts/into_junit.py | 5 +--- scripts/run_commit_tests.sh | 8 +++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100755 scripts/run_commit_tests.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000000..36094419103 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,46 @@ +name: CI Job to Generate JUnit Report + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + generate-report: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Make Shell Script Executable + run: chmod +x scripts/generate_input.sh + + - name: Run Shell Script to Generate Input File + run: | + ./scripts/run_commit_tests.sh + + - name: Run JUnit Report Generation Script + run: | + python scripts/into_junit.py /tmp/SHARED.UNITS > junit.xml + + - name: Upload JUnit Report + uses: actions/upload-artifact@v3 + with: + name: junit-report + path: junit.xml + + - name: Display JUnit Test Results + uses: dorny/test-reporter@v1 + with: + name: 'JUnit Results' + path: 'junit.xml' + reporter: 'junit' diff --git a/scripts/into_junit.py b/scripts/into_junit.py index c8eb6a57725..875e9c15f45 100644 --- a/scripts/into_junit.py +++ b/scripts/into_junit.py @@ -5,10 +5,7 @@ def create_testcase_element(testclass, testname, stdout, identifier, got, expected, status, url): testcase = ET.Element("testcase", classname=testclass, name=testname) - # Add properties with test description - properties = ET.SubElement(testcase, "properties") - property_element = ET.SubElement(properties, "property", name="test_description") - property_element.text = stdout + description = f"Test {identifier} with URL: {url}" if status == "PASS": system_out = ET.SubElement(testcase, "system-out") diff --git a/scripts/run_commit_tests.sh b/scripts/run_commit_tests.sh new file mode 100755 index 00000000000..de624c036f3 --- /dev/null +++ b/scripts/run_commit_tests.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# This script generates the input file used by the Python script. +# Replace the following lines with the actual commands to generate the input file. + +echo "| ANTI-REGRESSION.BC-COMP.01 | PASS |(https://example.com/test-report) | (assertEqualToResult (add-atom &kb (: axiom (nums 2 3)))) | (()) | (()) |" > /tmp/SHARED.UNITS + +# You can add more lines or commands to generate additional input data