mirror of
https://github.com/KevinMidboe/linguist.git
synced 2025-10-29 17:50:22 +00:00
@@ -765,8 +765,8 @@
|
|||||||
"exception.zep.php"
|
"exception.zep.php"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tokens_total": 637830,
|
"tokens_total": 639782,
|
||||||
"languages_total": 828,
|
"languages_total": 830,
|
||||||
"tokens": {
|
"tokens": {
|
||||||
"ABAP": {
|
"ABAP": {
|
||||||
"*/**": 1,
|
"*/**": 1,
|
||||||
@@ -53362,27 +53362,330 @@
|
|||||||
"TWO_PI": 1
|
"TWO_PI": 1
|
||||||
},
|
},
|
||||||
"Prolog": {
|
"Prolog": {
|
||||||
"-": 52,
|
"-": 161,
|
||||||
|
"module": 3,
|
||||||
|
"(": 327,
|
||||||
|
"format_spec": 12,
|
||||||
|
"[": 87,
|
||||||
|
"format_error/2": 1,
|
||||||
|
"format_spec/2": 1,
|
||||||
|
"format_spec//1": 1,
|
||||||
|
"spec_arity/2": 1,
|
||||||
|
"spec_types/2": 1,
|
||||||
|
"]": 87,
|
||||||
|
")": 326,
|
||||||
|
".": 107,
|
||||||
|
"use_module": 8,
|
||||||
|
"library": 8,
|
||||||
|
"dcg/basics": 1,
|
||||||
|
"eos//0": 1,
|
||||||
|
"integer//1": 1,
|
||||||
|
"string_without//2": 1,
|
||||||
|
"error": 6,
|
||||||
|
"when": 3,
|
||||||
|
"when/2": 1,
|
||||||
|
"%": 71,
|
||||||
|
"mavis": 1,
|
||||||
|
"format_error": 8,
|
||||||
|
"+": 14,
|
||||||
|
"Goal": 29,
|
||||||
|
"Error": 25,
|
||||||
|
"string": 8,
|
||||||
|
"is": 12,
|
||||||
|
"nondet.": 1,
|
||||||
|
"format": 8,
|
||||||
|
"Format": 23,
|
||||||
|
"Args": 19,
|
||||||
|
"format_error_": 5,
|
||||||
|
"_": 30,
|
||||||
|
"debug": 4,
|
||||||
|
"Spec": 10,
|
||||||
|
"is_list": 1,
|
||||||
|
"spec_types": 8,
|
||||||
|
"Types": 16,
|
||||||
|
"types_error": 3,
|
||||||
|
"length": 4,
|
||||||
|
"TypesLen": 3,
|
||||||
|
"ArgsLen": 3,
|
||||||
|
"types_error_": 4,
|
||||||
|
"Arg": 6,
|
||||||
|
"|": 25,
|
||||||
|
"Type": 3,
|
||||||
|
"ground": 5,
|
||||||
|
"is_of_type": 2,
|
||||||
|
"message_to_string": 1,
|
||||||
|
"type_error": 1,
|
||||||
|
"_Location": 1,
|
||||||
|
"multifile": 2,
|
||||||
|
"check": 3,
|
||||||
|
"checker/2.": 2,
|
||||||
|
"dynamic": 2,
|
||||||
|
"checker": 3,
|
||||||
|
"format_fail/3.": 1,
|
||||||
|
"prolog_walk_code": 1,
|
||||||
|
"module_class": 1,
|
||||||
|
"user": 5,
|
||||||
|
"infer_meta_predicates": 1,
|
||||||
|
"false": 2,
|
||||||
|
"autoload": 1,
|
||||||
|
"format/": 1,
|
||||||
|
"{": 7,
|
||||||
|
"}": 7,
|
||||||
|
"are": 3,
|
||||||
|
"always": 1,
|
||||||
|
"loaded": 1,
|
||||||
|
"undefined": 1,
|
||||||
|
"ignore": 1,
|
||||||
|
"trace_reference": 1,
|
||||||
|
"on_trace": 1,
|
||||||
|
"check_format": 3,
|
||||||
|
"retract": 1,
|
||||||
|
"format_fail": 2,
|
||||||
|
"Location": 6,
|
||||||
|
"print_message": 1,
|
||||||
|
"warning": 1,
|
||||||
|
"fail.": 3,
|
||||||
|
"iterate": 1,
|
||||||
|
"all": 1,
|
||||||
|
"errors": 2,
|
||||||
|
"checker.": 1,
|
||||||
|
"succeed": 2,
|
||||||
|
"even": 1,
|
||||||
|
"if": 1,
|
||||||
|
"no": 1,
|
||||||
|
"found": 1,
|
||||||
|
"Module": 4,
|
||||||
|
"_Caller": 1,
|
||||||
|
"predicate_property": 1,
|
||||||
|
"imported_from": 1,
|
||||||
|
"Source": 2,
|
||||||
|
"memberchk": 2,
|
||||||
|
"system": 1,
|
||||||
|
"prolog_debug": 1,
|
||||||
|
"can_check": 2,
|
||||||
|
"assert": 2,
|
||||||
|
"to": 5,
|
||||||
|
"avoid": 1,
|
||||||
|
"printing": 1,
|
||||||
|
"goals": 1,
|
||||||
|
"once": 3,
|
||||||
|
"clause": 1,
|
||||||
|
"prolog": 2,
|
||||||
|
"message": 1,
|
||||||
|
"message_location": 1,
|
||||||
|
"//": 1,
|
||||||
|
"eos.": 1,
|
||||||
|
"escape": 2,
|
||||||
|
"Numeric": 4,
|
||||||
|
"Modifier": 2,
|
||||||
|
"Action": 15,
|
||||||
|
"Rest": 12,
|
||||||
|
"numeric_argument": 5,
|
||||||
|
"modifier_argument": 3,
|
||||||
|
"action": 6,
|
||||||
|
"text": 4,
|
||||||
|
"String": 6,
|
||||||
|
";": 12,
|
||||||
|
"Codes": 21,
|
||||||
|
"string_codes": 4,
|
||||||
|
"string_without": 1,
|
||||||
|
"list": 4,
|
||||||
|
"semidet.": 3,
|
||||||
|
"text_codes": 6,
|
||||||
|
"phrase": 3,
|
||||||
|
"spec_arity": 2,
|
||||||
|
"FormatSpec": 2,
|
||||||
|
"Arity": 3,
|
||||||
|
"positive_integer": 1,
|
||||||
|
"det.": 4,
|
||||||
|
"type": 2,
|
||||||
|
"Item": 2,
|
||||||
|
"Items": 2,
|
||||||
|
"item_types": 3,
|
||||||
|
"numeric_types": 5,
|
||||||
|
"action_types": 18,
|
||||||
|
"number": 3,
|
||||||
|
"character": 2,
|
||||||
|
"star": 2,
|
||||||
|
"nothing": 2,
|
||||||
|
"atom_codes": 4,
|
||||||
|
"Code": 2,
|
||||||
|
"Text": 1,
|
||||||
|
"codes": 5,
|
||||||
|
"Var": 5,
|
||||||
|
"var": 4,
|
||||||
|
"Atom": 3,
|
||||||
|
"atom": 6,
|
||||||
|
"N": 5,
|
||||||
|
"integer": 7,
|
||||||
|
"C": 5,
|
||||||
|
"colon": 1,
|
||||||
|
"no_colon": 1,
|
||||||
|
"is_action": 4,
|
||||||
|
"multi.": 1,
|
||||||
|
"a": 4,
|
||||||
|
"d": 3,
|
||||||
|
"e": 1,
|
||||||
|
"float": 3,
|
||||||
|
"f": 1,
|
||||||
|
"G": 2,
|
||||||
|
"I": 1,
|
||||||
|
"n": 1,
|
||||||
|
"p": 1,
|
||||||
|
"any": 3,
|
||||||
|
"r": 1,
|
||||||
|
"s": 2,
|
||||||
|
"t": 1,
|
||||||
|
"W": 1,
|
||||||
|
"func": 13,
|
||||||
|
"op": 2,
|
||||||
|
"xfy": 2,
|
||||||
|
"of": 8,
|
||||||
|
"/2": 3,
|
||||||
|
"list_util": 1,
|
||||||
|
"xfy_list/3": 1,
|
||||||
|
"function_expansion": 5,
|
||||||
|
"arithmetic": 2,
|
||||||
|
"wants_func": 4,
|
||||||
|
"prolog_load_context": 1,
|
||||||
|
"we": 1,
|
||||||
|
"don": 1,
|
||||||
|
"used": 1,
|
||||||
|
"at": 3,
|
||||||
|
"compile": 3,
|
||||||
|
"time": 3,
|
||||||
|
"for": 1,
|
||||||
|
"macro": 1,
|
||||||
|
"expansion.": 1,
|
||||||
|
"compile_function/4.": 1,
|
||||||
|
"compile_function": 8,
|
||||||
|
"Expr": 5,
|
||||||
|
"In": 15,
|
||||||
|
"Out": 16,
|
||||||
|
"evaluable/1": 1,
|
||||||
|
"throws": 1,
|
||||||
|
"exception": 1,
|
||||||
|
"with": 2,
|
||||||
|
"strings": 1,
|
||||||
|
"evaluable": 1,
|
||||||
|
"term_variables": 1,
|
||||||
|
"F": 26,
|
||||||
|
"function_composition_term": 2,
|
||||||
|
"Functor": 8,
|
||||||
|
"true": 5,
|
||||||
|
"..": 6,
|
||||||
|
"format_template": 7,
|
||||||
|
"has_type": 2,
|
||||||
|
"fail": 1,
|
||||||
|
"be": 4,
|
||||||
|
"explicit": 1,
|
||||||
|
"Dict": 3,
|
||||||
|
"is_dict": 1,
|
||||||
|
"get_dict": 1,
|
||||||
|
"Function": 5,
|
||||||
|
"Argument": 1,
|
||||||
|
"Apply": 1,
|
||||||
|
"an": 1,
|
||||||
|
"Argument.": 1,
|
||||||
|
"A": 1,
|
||||||
|
"predicate": 4,
|
||||||
|
"whose": 2,
|
||||||
|
"final": 1,
|
||||||
|
"argument": 2,
|
||||||
|
"generates": 1,
|
||||||
|
"output": 1,
|
||||||
|
"and": 2,
|
||||||
|
"penultimate": 1,
|
||||||
|
"accepts": 1,
|
||||||
|
"input.": 1,
|
||||||
|
"This": 1,
|
||||||
|
"realized": 1,
|
||||||
|
"by": 2,
|
||||||
|
"expanding": 1,
|
||||||
|
"function": 2,
|
||||||
|
"application": 2,
|
||||||
|
"chained": 1,
|
||||||
|
"calls": 1,
|
||||||
|
"time.": 1,
|
||||||
|
"itself": 1,
|
||||||
|
"can": 3,
|
||||||
|
"chained.": 2,
|
||||||
|
"Reversed": 2,
|
||||||
|
"reverse": 4,
|
||||||
|
"sort": 2,
|
||||||
|
"c": 2,
|
||||||
|
"b": 4,
|
||||||
|
"meta_predicate": 2,
|
||||||
|
"throw": 1,
|
||||||
|
"permission_error": 1,
|
||||||
|
"call": 4,
|
||||||
|
"context": 1,
|
||||||
|
"X": 10,
|
||||||
|
"Y": 7,
|
||||||
|
"defer": 1,
|
||||||
|
"until": 1,
|
||||||
|
"run": 1,
|
||||||
|
"P": 2,
|
||||||
|
"Creates": 1,
|
||||||
|
"new": 2,
|
||||||
|
"composing": 1,
|
||||||
|
"G.": 1,
|
||||||
|
"The": 1,
|
||||||
|
"functions": 2,
|
||||||
|
"composed": 1,
|
||||||
|
"create": 1,
|
||||||
|
"compiled": 1,
|
||||||
|
"which": 1,
|
||||||
|
"behaves": 1,
|
||||||
|
"like": 1,
|
||||||
|
"function.": 1,
|
||||||
|
"composition": 1,
|
||||||
|
"Composed": 1,
|
||||||
|
"also": 1,
|
||||||
|
"applied": 1,
|
||||||
|
"/2.": 1,
|
||||||
|
"fix": 1,
|
||||||
|
"syntax": 1,
|
||||||
|
"highlighting": 1,
|
||||||
|
"functions_to_compose": 2,
|
||||||
|
"Term": 10,
|
||||||
|
"Funcs": 7,
|
||||||
|
"functor": 1,
|
||||||
|
"Op": 3,
|
||||||
|
"xfy_list": 2,
|
||||||
|
"thread_state": 4,
|
||||||
|
"Goals": 2,
|
||||||
|
"Tmp": 3,
|
||||||
|
"instantiation_error": 1,
|
||||||
|
"append": 2,
|
||||||
|
"NewArgs": 2,
|
||||||
|
"variant_sha1": 1,
|
||||||
|
"Sha": 2,
|
||||||
|
"current_predicate": 1,
|
||||||
|
"Functor/2": 2,
|
||||||
|
"RevFuncs": 2,
|
||||||
|
"Threaded": 2,
|
||||||
|
"Body": 2,
|
||||||
|
"Head": 2,
|
||||||
|
"compile_predicates": 1,
|
||||||
|
"Output": 2,
|
||||||
|
"compound": 1,
|
||||||
|
"setof": 1,
|
||||||
|
"arg": 1,
|
||||||
|
"Name": 2,
|
||||||
|
"Args0": 2,
|
||||||
|
"nth1": 2,
|
||||||
"lib": 1,
|
"lib": 1,
|
||||||
"(": 49,
|
|
||||||
"ic": 1,
|
"ic": 1,
|
||||||
")": 49,
|
|
||||||
".": 25,
|
|
||||||
"vabs": 2,
|
"vabs": 2,
|
||||||
"Val": 8,
|
"Val": 8,
|
||||||
"AbsVal": 10,
|
"AbsVal": 10,
|
||||||
"#": 9,
|
"#": 9,
|
||||||
";": 1,
|
|
||||||
"labeling": 2,
|
"labeling": 2,
|
||||||
"[": 21,
|
|
||||||
"]": 21,
|
|
||||||
"vabsIC": 1,
|
"vabsIC": 1,
|
||||||
"or": 1,
|
"or": 1,
|
||||||
"faitListe": 3,
|
"faitListe": 3,
|
||||||
"_": 2,
|
|
||||||
"First": 2,
|
"First": 2,
|
||||||
"|": 12,
|
|
||||||
"Rest": 6,
|
|
||||||
"Taille": 2,
|
"Taille": 2,
|
||||||
"Min": 2,
|
"Min": 2,
|
||||||
"Max": 2,
|
"Max": 2,
|
||||||
@@ -53397,7 +53700,6 @@
|
|||||||
"Xi.": 1,
|
"Xi.": 1,
|
||||||
"checkPeriode": 3,
|
"checkPeriode": 3,
|
||||||
"ListVar": 2,
|
"ListVar": 2,
|
||||||
"length": 1,
|
|
||||||
"Length": 2,
|
"Length": 2,
|
||||||
"<": 1,
|
"<": 1,
|
||||||
"X1": 2,
|
"X1": 2,
|
||||||
@@ -53418,9 +53720,6 @@
|
|||||||
"christie": 3,
|
"christie": 3,
|
||||||
"parents": 4,
|
"parents": 4,
|
||||||
"brother": 1,
|
"brother": 1,
|
||||||
"X": 3,
|
|
||||||
"Y": 2,
|
|
||||||
"F": 2,
|
|
||||||
"M": 2,
|
"M": 2,
|
||||||
"turing": 1,
|
"turing": 1,
|
||||||
"Tape0": 2,
|
"Tape0": 2,
|
||||||
@@ -53429,9 +53728,7 @@
|
|||||||
"q0": 1,
|
"q0": 1,
|
||||||
"Ls": 12,
|
"Ls": 12,
|
||||||
"Rs": 16,
|
"Rs": 16,
|
||||||
"reverse": 1,
|
|
||||||
"Ls1": 4,
|
"Ls1": 4,
|
||||||
"append": 1,
|
|
||||||
"qf": 1,
|
"qf": 1,
|
||||||
"Q0": 2,
|
"Q0": 2,
|
||||||
"Ls0": 6,
|
"Ls0": 6,
|
||||||
@@ -53439,14 +53736,10 @@
|
|||||||
"symbol": 3,
|
"symbol": 3,
|
||||||
"Sym": 6,
|
"Sym": 6,
|
||||||
"RsRest": 2,
|
"RsRest": 2,
|
||||||
"once": 1,
|
|
||||||
"rule": 1,
|
"rule": 1,
|
||||||
"Q1": 2,
|
"Q1": 2,
|
||||||
"NewSym": 2,
|
"NewSym": 2,
|
||||||
"Action": 2,
|
|
||||||
"action": 4,
|
|
||||||
"Rs1": 2,
|
"Rs1": 2,
|
||||||
"b": 2,
|
|
||||||
"left": 4,
|
"left": 4,
|
||||||
"stay": 1,
|
"stay": 1,
|
||||||
"right": 1,
|
"right": 1,
|
||||||
@@ -68898,7 +69191,7 @@
|
|||||||
"PostScript": 107,
|
"PostScript": 107,
|
||||||
"PowerShell": 12,
|
"PowerShell": 12,
|
||||||
"Processing": 74,
|
"Processing": 74,
|
||||||
"Prolog": 468,
|
"Prolog": 2420,
|
||||||
"Propeller Spin": 13519,
|
"Propeller Spin": 13519,
|
||||||
"Protocol Buffer": 63,
|
"Protocol Buffer": 63,
|
||||||
"PureScript": 1652,
|
"PureScript": 1652,
|
||||||
@@ -69092,7 +69385,7 @@
|
|||||||
"PostScript": 1,
|
"PostScript": 1,
|
||||||
"PowerShell": 2,
|
"PowerShell": 2,
|
||||||
"Processing": 1,
|
"Processing": 1,
|
||||||
"Prolog": 3,
|
"Prolog": 5,
|
||||||
"Propeller Spin": 10,
|
"Propeller Spin": 10,
|
||||||
"Protocol Buffer": 1,
|
"Protocol Buffer": 1,
|
||||||
"PureScript": 4,
|
"PureScript": 4,
|
||||||
@@ -69155,5 +69448,5 @@
|
|||||||
"Zephir": 5,
|
"Zephir": 5,
|
||||||
"Zimpl": 1
|
"Zimpl": 1
|
||||||
},
|
},
|
||||||
"md5": "896b4ca841571551a8fe421eec69b0f6"
|
"md5": "95cec2f85e2b8d7b956746aab7aa16aa"
|
||||||
}
|
}
|
||||||
260
samples/Prolog/format_spec.pl
Normal file
260
samples/Prolog/format_spec.pl
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
:- module(format_spec, [ format_error/2
|
||||||
|
, format_spec/2
|
||||||
|
, format_spec//1
|
||||||
|
, spec_arity/2
|
||||||
|
, spec_types/2
|
||||||
|
]).
|
||||||
|
|
||||||
|
:- use_module(library(dcg/basics), [eos//0, integer//1, string_without//2]).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
:- use_module(library(when), [when/2]).
|
||||||
|
|
||||||
|
% TODO loading this module is optional
|
||||||
|
% TODO it's for my own convenience during development
|
||||||
|
%:- use_module(library(mavis)).
|
||||||
|
|
||||||
|
%% format_error(+Goal, -Error:string) is nondet.
|
||||||
|
%
|
||||||
|
% True if Goal exhibits an Error in its format string. The
|
||||||
|
% Error string describes what is wrong with Goal. Iterates each
|
||||||
|
% error on backtracking.
|
||||||
|
%
|
||||||
|
% Goal may be one of the following predicates:
|
||||||
|
%
|
||||||
|
% * format/2
|
||||||
|
% * format/3
|
||||||
|
% * debug/3
|
||||||
|
format_error(format(Format,Args), Error) :-
|
||||||
|
format_error_(Format, Args,Error).
|
||||||
|
format_error(format(_,Format,Args), Error) :-
|
||||||
|
format_error_(Format,Args,Error).
|
||||||
|
format_error(debug(_,Format,Args), Error) :-
|
||||||
|
format_error_(Format,Args,Error).
|
||||||
|
|
||||||
|
format_error_(Format,Args,Error) :-
|
||||||
|
format_spec(Format, Spec),
|
||||||
|
!,
|
||||||
|
is_list(Args),
|
||||||
|
spec_types(Spec, Types),
|
||||||
|
types_error(Args, Types, Error).
|
||||||
|
format_error_(Format,_,Error) :-
|
||||||
|
% \+ format_spec(Format, _),
|
||||||
|
format(string(Error), "Invalid format string: ~q", [Format]).
|
||||||
|
|
||||||
|
types_error(Args, Types, Error) :-
|
||||||
|
length(Types, TypesLen),
|
||||||
|
length(Args, ArgsLen),
|
||||||
|
TypesLen =\= ArgsLen,
|
||||||
|
!,
|
||||||
|
format( string(Error)
|
||||||
|
, "Wrong argument count. Expected ~d, got ~d"
|
||||||
|
, [TypesLen, ArgsLen]
|
||||||
|
).
|
||||||
|
types_error(Args, Types, Error) :-
|
||||||
|
types_error_(Args, Types, Error).
|
||||||
|
|
||||||
|
types_error_([Arg|_],[Type|_],Error) :-
|
||||||
|
ground(Arg),
|
||||||
|
\+ is_of_type(Type,Arg),
|
||||||
|
message_to_string(error(type_error(Type,Arg),_Location),Error).
|
||||||
|
types_error_([_|Args],[_|Types],Error) :-
|
||||||
|
types_error_(Args, Types, Error).
|
||||||
|
|
||||||
|
|
||||||
|
% check/0 augmentation
|
||||||
|
:- multifile check:checker/2.
|
||||||
|
:- dynamic check:checker/2.
|
||||||
|
check:checker(format_spec:checker, "format/2 strings and arguments").
|
||||||
|
|
||||||
|
:- dynamic format_fail/3.
|
||||||
|
|
||||||
|
checker :-
|
||||||
|
prolog_walk_code([ module_class([user])
|
||||||
|
, infer_meta_predicates(false)
|
||||||
|
, autoload(false) % format/{2,3} are always loaded
|
||||||
|
, undefined(ignore)
|
||||||
|
, trace_reference(_)
|
||||||
|
, on_trace(check_format)
|
||||||
|
]),
|
||||||
|
retract(format_fail(Goal,Location,Error)),
|
||||||
|
print_message(warning, format_error(Goal,Location,Error)),
|
||||||
|
fail. % iterate all errors
|
||||||
|
checker. % succeed even if no errors are found
|
||||||
|
|
||||||
|
check_format(Module:Goal, _Caller, Location) :-
|
||||||
|
predicate_property(Module:Goal, imported_from(Source)),
|
||||||
|
memberchk(Source, [system,prolog_debug]),
|
||||||
|
can_check(Goal),
|
||||||
|
format_error(Goal, Error),
|
||||||
|
assert(format_fail(Goal, Location, Error)),
|
||||||
|
fail.
|
||||||
|
check_format(_,_,_). % succeed to avoid printing goals
|
||||||
|
|
||||||
|
% true if format_error/2 can check this goal
|
||||||
|
can_check(Goal) :-
|
||||||
|
once(clause(format_error(Goal,_),_)).
|
||||||
|
|
||||||
|
prolog:message(format_error(Goal,Location,Error)) -->
|
||||||
|
prolog:message_location(Location),
|
||||||
|
['~n In goal: ~q~n ~s'-[Goal,Error]].
|
||||||
|
|
||||||
|
|
||||||
|
%% format_spec(-Spec)//
|
||||||
|
%
|
||||||
|
% DCG for parsing format strings. It doesn't yet generate format
|
||||||
|
% strings from a spec. See format_spec/2 for details.
|
||||||
|
format_spec([]) -->
|
||||||
|
eos.
|
||||||
|
format_spec([escape(Numeric,Modifier,Action)|Rest]) -->
|
||||||
|
"~",
|
||||||
|
numeric_argument(Numeric),
|
||||||
|
modifier_argument(Modifier),
|
||||||
|
action(Action),
|
||||||
|
format_spec(Rest).
|
||||||
|
format_spec([text(String)|Rest]) -->
|
||||||
|
{ when((ground(String);ground(Codes)),string_codes(String, Codes)) },
|
||||||
|
string_without("~", Codes),
|
||||||
|
{ Codes \= [] },
|
||||||
|
format_spec(Rest).
|
||||||
|
|
||||||
|
|
||||||
|
%% format_spec(+Format, -Spec:list) is semidet.
|
||||||
|
%
|
||||||
|
% Parse a format string. Each element of Spec is one of the following:
|
||||||
|
%
|
||||||
|
% * `text(Text)` - text sent to the output as is
|
||||||
|
% * `escape(Num,Colon,Action)` - a format escape
|
||||||
|
%
|
||||||
|
% `Num` represents the optional numeric portion of an esape. `Colon`
|
||||||
|
% represents the optional colon in an escape. `Action` is an atom
|
||||||
|
% representing the action to be take by this escape.
|
||||||
|
format_spec(Format, Spec) :-
|
||||||
|
when((ground(Format);ground(Codes)),text_codes(Format, Codes)),
|
||||||
|
once(phrase(format_spec(Spec), Codes, [])).
|
||||||
|
|
||||||
|
%% spec_arity(+FormatSpec, -Arity:positive_integer) is det.
|
||||||
|
%
|
||||||
|
% True if FormatSpec requires format/2 to have Arity arguments.
|
||||||
|
spec_arity(Spec, Arity) :-
|
||||||
|
spec_types(Spec, Types),
|
||||||
|
length(Types, Arity).
|
||||||
|
|
||||||
|
|
||||||
|
%% spec_types(+FormatSpec, -Types:list(type)) is det.
|
||||||
|
%
|
||||||
|
% True if FormatSpec requires format/2 to have arguments of Types. Each
|
||||||
|
% value of Types is a type as described by error:has_type/2. This
|
||||||
|
% notion of types is compatible with library(mavis).
|
||||||
|
spec_types(Spec, Types) :-
|
||||||
|
phrase(spec_types(Spec), Types).
|
||||||
|
|
||||||
|
spec_types([]) -->
|
||||||
|
[].
|
||||||
|
spec_types([Item|Items]) -->
|
||||||
|
item_types(Item),
|
||||||
|
spec_types(Items).
|
||||||
|
|
||||||
|
item_types(text(_)) -->
|
||||||
|
[].
|
||||||
|
item_types(escape(Numeric,_,Action)) -->
|
||||||
|
numeric_types(Numeric),
|
||||||
|
action_types(Action).
|
||||||
|
|
||||||
|
numeric_types(number(_)) -->
|
||||||
|
[].
|
||||||
|
numeric_types(character(_)) -->
|
||||||
|
[].
|
||||||
|
numeric_types(star) -->
|
||||||
|
[number].
|
||||||
|
numeric_types(nothing) -->
|
||||||
|
[].
|
||||||
|
|
||||||
|
action_types(Action) -->
|
||||||
|
{ atom_codes(Action, [Code]) },
|
||||||
|
{ action_types(Code, Types) },
|
||||||
|
phrase(Types).
|
||||||
|
|
||||||
|
|
||||||
|
%% text_codes(Text:text, Codes:codes).
|
||||||
|
text_codes(Var, Codes) :-
|
||||||
|
var(Var),
|
||||||
|
!,
|
||||||
|
string_codes(Var, Codes).
|
||||||
|
text_codes(Atom, Codes) :-
|
||||||
|
atom(Atom),
|
||||||
|
!,
|
||||||
|
atom_codes(Atom, Codes).
|
||||||
|
text_codes(String, Codes) :-
|
||||||
|
string(String),
|
||||||
|
!,
|
||||||
|
string_codes(String, Codes).
|
||||||
|
text_codes(Codes, Codes) :-
|
||||||
|
is_of_type(codes, Codes).
|
||||||
|
|
||||||
|
|
||||||
|
numeric_argument(number(N)) -->
|
||||||
|
integer(N).
|
||||||
|
numeric_argument(character(C)) -->
|
||||||
|
"`",
|
||||||
|
[C].
|
||||||
|
numeric_argument(star) -->
|
||||||
|
"*".
|
||||||
|
numeric_argument(nothing) -->
|
||||||
|
"".
|
||||||
|
|
||||||
|
|
||||||
|
modifier_argument(colon) -->
|
||||||
|
":".
|
||||||
|
modifier_argument(no_colon) -->
|
||||||
|
\+ ":".
|
||||||
|
|
||||||
|
|
||||||
|
action(Action) -->
|
||||||
|
[C],
|
||||||
|
{ is_action(C) },
|
||||||
|
{ atom_codes(Action, [C]) }.
|
||||||
|
|
||||||
|
|
||||||
|
%% is_action(+Action:integer) is semidet.
|
||||||
|
%% is_action(-Action:integer) is multi.
|
||||||
|
%
|
||||||
|
% True if Action is a valid format/2 action character. Iterates all
|
||||||
|
% acceptable action characters, if Action is unbound.
|
||||||
|
is_action(Action) :-
|
||||||
|
action_types(Action, _).
|
||||||
|
|
||||||
|
%% action_types(?Action:integer, ?Types:list(type))
|
||||||
|
%
|
||||||
|
% True if Action consumes arguments matching Types. An action (like
|
||||||
|
% `~`), which consumes no arguments, has `Types=[]`. For example,
|
||||||
|
%
|
||||||
|
% ?- action_types(0'~, Types).
|
||||||
|
% Types = [].
|
||||||
|
% ?- action_types(0'a, Types).
|
||||||
|
% Types = [atom].
|
||||||
|
action_types(0'~, []).
|
||||||
|
action_types(0'a, [atom]).
|
||||||
|
action_types(0'c, [integer]). % specifically, a code
|
||||||
|
action_types(0'd, [integer]).
|
||||||
|
action_types(0'D, [integer]).
|
||||||
|
action_types(0'e, [float]).
|
||||||
|
action_types(0'E, [float]).
|
||||||
|
action_types(0'f, [float]).
|
||||||
|
action_types(0'g, [float]).
|
||||||
|
action_types(0'G, [float]).
|
||||||
|
action_types(0'i, [any]).
|
||||||
|
action_types(0'I, [integer]).
|
||||||
|
action_types(0'k, [any]).
|
||||||
|
action_types(0'n, []).
|
||||||
|
action_types(0'N, []).
|
||||||
|
action_types(0'p, [any]).
|
||||||
|
action_types(0'q, [any]).
|
||||||
|
action_types(0'r, [integer]).
|
||||||
|
action_types(0'R, [integer]).
|
||||||
|
action_types(0's, [text]).
|
||||||
|
action_types(0'@, [callable]).
|
||||||
|
action_types(0't, []).
|
||||||
|
action_types(0'|, []).
|
||||||
|
action_types(0'+, []).
|
||||||
|
action_types(0'w, [any]).
|
||||||
|
action_types(0'W, [any, list]).
|
||||||
194
samples/Prolog/func.pl
Normal file
194
samples/Prolog/func.pl
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
:- module(func, [ op(675, xfy, ($))
|
||||||
|
, op(650, xfy, (of))
|
||||||
|
, ($)/2
|
||||||
|
, (of)/2
|
||||||
|
]).
|
||||||
|
:- use_module(library(list_util), [xfy_list/3]).
|
||||||
|
:- use_module(library(function_expansion)).
|
||||||
|
:- use_module(library(arithmetic)).
|
||||||
|
:- use_module(library(error)).
|
||||||
|
|
||||||
|
|
||||||
|
% true if the module whose terms are being read has specifically
|
||||||
|
% imported library(func).
|
||||||
|
wants_func :-
|
||||||
|
prolog_load_context(module, Module),
|
||||||
|
Module \== func, % we don't want func sugar ourselves
|
||||||
|
predicate_property(Module:of(_,_),imported_from(func)).
|
||||||
|
|
||||||
|
|
||||||
|
%% compile_function(+Term, -In, -Out, -Goal) is semidet.
|
||||||
|
%
|
||||||
|
% True if Term represents a function from In to Out
|
||||||
|
% implemented by calling Goal. This multifile hook is
|
||||||
|
% called by $/2 and of/2 to convert a term into a goal.
|
||||||
|
% It's used at compile time for macro expansion.
|
||||||
|
% It's used at run time to handle functions which aren't
|
||||||
|
% known at compile time.
|
||||||
|
% When called as a hook, Term is guaranteed to be =nonvar=.
|
||||||
|
%
|
||||||
|
% For example, to treat library(assoc) terms as functions which
|
||||||
|
% map a key to a value, one might define:
|
||||||
|
%
|
||||||
|
% :- multifile compile_function/4.
|
||||||
|
% compile_function(Assoc, Key, Value, Goal) :-
|
||||||
|
% is_assoc(Assoc),
|
||||||
|
% Goal = get_assoc(Key, Assoc, Value).
|
||||||
|
%
|
||||||
|
% Then one could write:
|
||||||
|
%
|
||||||
|
% list_to_assoc([a-1, b-2, c-3], Assoc),
|
||||||
|
% Two = Assoc $ b,
|
||||||
|
:- multifile compile_function/4.
|
||||||
|
compile_function(Var, _, _, _) :-
|
||||||
|
% variables storing functions must be evaluated at run time
|
||||||
|
% and can't be compiled, a priori, into a goal
|
||||||
|
var(Var),
|
||||||
|
!,
|
||||||
|
fail.
|
||||||
|
compile_function(Expr, In, Out, Out is Expr) :-
|
||||||
|
% arithmetic expression of one variable are simply evaluated
|
||||||
|
\+ string(Expr), % evaluable/1 throws exception with strings
|
||||||
|
arithmetic:evaluable(Expr),
|
||||||
|
term_variables(Expr, [In]).
|
||||||
|
compile_function(F, In, Out, func:Goal) :-
|
||||||
|
% composed functions
|
||||||
|
function_composition_term(F),
|
||||||
|
user:function_expansion(F, func:Functor, true),
|
||||||
|
Goal =.. [Functor,In,Out].
|
||||||
|
compile_function(F, In, Out, Goal) :-
|
||||||
|
% string interpolation via format templates
|
||||||
|
format_template(F),
|
||||||
|
( atom(F) ->
|
||||||
|
Goal = format(atom(Out), F, In)
|
||||||
|
; string(F) ->
|
||||||
|
Goal = format(string(Out), F, In)
|
||||||
|
; error:has_type(codes, F) ->
|
||||||
|
Goal = format(codes(Out), F, In)
|
||||||
|
; fail % to be explicit
|
||||||
|
).
|
||||||
|
compile_function(Dict, In, Out, Goal) :-
|
||||||
|
is_dict(Dict),
|
||||||
|
Goal = get_dict(In, Dict, Out).
|
||||||
|
|
||||||
|
%% $(+Function, +Argument) is det.
|
||||||
|
%
|
||||||
|
% Apply Function to an Argument. A Function is any predicate
|
||||||
|
% whose final argument generates output and whose penultimate argument
|
||||||
|
% accepts input.
|
||||||
|
%
|
||||||
|
% This is realized by expanding function application to chained
|
||||||
|
% predicate calls at compile time. Function application itself can
|
||||||
|
% be chained.
|
||||||
|
%
|
||||||
|
% ==
|
||||||
|
% Reversed = reverse $ sort $ [c,d,b].
|
||||||
|
% ==
|
||||||
|
:- meta_predicate $(2,+).
|
||||||
|
$(_,_) :-
|
||||||
|
throw(error(permission_error(call, predicate, ($)/2),
|
||||||
|
context(_, '$/2 must be subject to goal expansion'))).
|
||||||
|
|
||||||
|
user:function_expansion($(F,X), Y, Goal) :-
|
||||||
|
wants_func,
|
||||||
|
( func:compile_function(F, X, Y, Goal) ->
|
||||||
|
true
|
||||||
|
; var(F) -> Goal = % defer until run time
|
||||||
|
( func:compile_function(F, X, Y, P) ->
|
||||||
|
call(P)
|
||||||
|
; call(F, X, Y)
|
||||||
|
)
|
||||||
|
; Goal = call(F, X, Y)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
%% of(+F, +G) is det.
|
||||||
|
%
|
||||||
|
% Creates a new function by composing F and G. The functions are
|
||||||
|
% composed at compile time to create a new, compiled predicate which
|
||||||
|
% behaves like a function. Function composition can be chained.
|
||||||
|
% Composed functions can also be applied with $/2.
|
||||||
|
%
|
||||||
|
% ==
|
||||||
|
% Reversed = reverse of sort $ [c,d,b].
|
||||||
|
% ==
|
||||||
|
:- meta_predicate of(2,2).
|
||||||
|
of(_,_).
|
||||||
|
|
||||||
|
|
||||||
|
%% format_template(Format) is semidet.
|
||||||
|
%
|
||||||
|
% True if Format is a template string suitable for format/3.
|
||||||
|
% The current check is very naive and should be improved.
|
||||||
|
format_template(Format) :-
|
||||||
|
atom(Format), !,
|
||||||
|
atom_codes(Format, Codes),
|
||||||
|
format_template(Codes).
|
||||||
|
format_template(Format) :-
|
||||||
|
string(Format),
|
||||||
|
!,
|
||||||
|
string_codes(Format, Codes),
|
||||||
|
format_template(Codes).
|
||||||
|
format_template(Format) :-
|
||||||
|
error:has_type(codes, Format),
|
||||||
|
memberchk(0'~, Format). % ' fix syntax highlighting
|
||||||
|
|
||||||
|
|
||||||
|
% True if the argument is a function composition term
|
||||||
|
function_composition_term(of(_,_)).
|
||||||
|
|
||||||
|
% Converts a function composition term into a list of functions to compose
|
||||||
|
functions_to_compose(Term, Funcs) :-
|
||||||
|
functor(Term, Op, 2),
|
||||||
|
Op = (of),
|
||||||
|
xfy_list(Op, Term, Funcs).
|
||||||
|
|
||||||
|
% Thread a state variable through a list of functions. This is similar
|
||||||
|
% to a DCG expansion, but much simpler.
|
||||||
|
thread_state([], [], Out, Out).
|
||||||
|
thread_state([F|Funcs], [Goal|Goals], In, Out) :-
|
||||||
|
( compile_function(F, In, Tmp, Goal) ->
|
||||||
|
true
|
||||||
|
; var(F) ->
|
||||||
|
instantiation_error(F)
|
||||||
|
; F =.. [Functor|Args],
|
||||||
|
append(Args, [In, Tmp], NewArgs),
|
||||||
|
Goal =.. [Functor|NewArgs]
|
||||||
|
),
|
||||||
|
thread_state(Funcs, Goals, Tmp, Out).
|
||||||
|
|
||||||
|
user:function_expansion(Term, func:Functor, true) :-
|
||||||
|
wants_func,
|
||||||
|
functions_to_compose(Term, Funcs),
|
||||||
|
debug(func, 'building composed function for: ~w', [Term]),
|
||||||
|
variant_sha1(Funcs, Sha),
|
||||||
|
format(atom(Functor), 'composed_function_~w', [Sha]),
|
||||||
|
debug(func, ' name: ~s', [Functor]),
|
||||||
|
( func:current_predicate(Functor/2) ->
|
||||||
|
debug(func, ' composed predicate already exists', [])
|
||||||
|
; true ->
|
||||||
|
reverse(Funcs, RevFuncs),
|
||||||
|
thread_state(RevFuncs, Threaded, In, Out),
|
||||||
|
xfy_list(',', Body, Threaded),
|
||||||
|
Head =.. [Functor, In, Out],
|
||||||
|
func:assert(Head :- Body),
|
||||||
|
func:compile_predicates([Functor/2])
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
% support foo(x,~,y) evaluation
|
||||||
|
user:function_expansion(Term, Output, Goal) :-
|
||||||
|
wants_func,
|
||||||
|
compound(Term),
|
||||||
|
|
||||||
|
% has a single ~ argument
|
||||||
|
setof( X
|
||||||
|
, ( arg(X,Term,Arg), Arg == '~' )
|
||||||
|
, [N]
|
||||||
|
),
|
||||||
|
|
||||||
|
% replace ~ with a variable
|
||||||
|
Term =.. [Name|Args0],
|
||||||
|
nth1(N, Args0, ~, Rest),
|
||||||
|
nth1(N, Args, Output, Rest),
|
||||||
|
Goal =.. [Name|Args].
|
||||||
Reference in New Issue
Block a user