yukigo-prolog-parser 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,219 @@
1
+ // Generated automatically by nearley, version 2.20.1
2
+ // http://github.com/Hardmath123/nearley
3
+ // Bypasses TS6133. Allow declared but unused functions.
4
+ // @ts-ignore
5
+ function id(d: any[]): any { return d[0]; }
6
+ declare var period: any;
7
+ declare var colonDash: any;
8
+ declare var queryOp: any;
9
+ declare var comma: any;
10
+ declare var semicolon: any;
11
+ declare var forallRule: any;
12
+ declare var findallRule: any;
13
+ declare var notOperator: any;
14
+ declare var lparen: any;
15
+ declare var rparen: any;
16
+ declare var consOp: any;
17
+ declare var wildcard: any;
18
+ declare var variable: any;
19
+ declare var atom: any;
20
+ declare var op: any;
21
+ declare var comparisonOp: any;
22
+ declare var number: any;
23
+ declare var string: any;
24
+ declare var WS: any;
25
+
26
+ import {
27
+ NumberPrimitive,
28
+ StringPrimitive,
29
+ Rule,
30
+ Fact,
31
+ Query,
32
+ ConsExpression,
33
+ ArithmeticBinaryOperation,
34
+ ArithmeticUnaryOperation,
35
+ ComparisonOperation,
36
+ Exist,
37
+ Not,
38
+ Findall,
39
+ Sequence,
40
+ If,
41
+ Call,
42
+ Forall,
43
+ SymbolPrimitive,
44
+ AssignOperation,
45
+ UnifyOperation,
46
+ VariablePattern,
47
+ FunctorPattern,
48
+ ConsPattern,
49
+ ListPattern,
50
+ LiteralPattern,
51
+ WildcardPattern,
52
+ TuplePattern
53
+ } from "yukigo-ast"
54
+
55
+ import { PrologLexer } from "./lexer.js"
56
+
57
+ const asSequence = (d) => d.length === 1 ? d[0] : new Sequence(d);
58
+
59
+
60
+ interface NearleyToken {
61
+ value: any;
62
+ [key: string]: any;
63
+ };
64
+
65
+ interface NearleyLexer {
66
+ reset: (chunk: string, info: any) => void;
67
+ next: () => NearleyToken | undefined;
68
+ save: () => any;
69
+ formatError: (token: never) => string;
70
+ has: (tokenType: string) => boolean;
71
+ };
72
+
73
+ interface NearleyRule {
74
+ name: string;
75
+ symbols: NearleySymbol[];
76
+ postprocess?: (d: any[], loc?: number, reject?: {}) => any;
77
+ };
78
+
79
+ type NearleySymbol = string | { literal: any } | { test: (token: any) => boolean };
80
+
81
+ interface Grammar {
82
+ Lexer: NearleyLexer | undefined;
83
+ ParserRules: NearleyRule[];
84
+ ParserStart: string;
85
+ };
86
+
87
+ const grammar: Grammar = {
88
+ Lexer: PrologLexer,
89
+ ParserRules: [
90
+ {"name": "program$ebnf$1", "symbols": []},
91
+ {"name": "program$ebnf$1$subexpression$1", "symbols": ["clause", "_"]},
92
+ {"name": "program$ebnf$1", "symbols": ["program$ebnf$1", "program$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
93
+ {"name": "program", "symbols": ["program$ebnf$1"], "postprocess": (d) => d[0].map(x => x[0]).filter(x => x !== null).flat(Infinity)},
94
+ {"name": "clause$subexpression$1", "symbols": ["fact"]},
95
+ {"name": "clause$subexpression$1", "symbols": ["rule"]},
96
+ {"name": "clause$subexpression$1", "symbols": ["query"]},
97
+ {"name": "clause", "symbols": ["clause$subexpression$1"], "postprocess": (d) => d[0][0]},
98
+ {"name": "fact$ebnf$1", "symbols": ["arguments"], "postprocess": id},
99
+ {"name": "fact$ebnf$1", "symbols": [], "postprocess": () => null},
100
+ {"name": "fact", "symbols": ["any_atom", "fact$ebnf$1", "_", (PrologLexer.has("period") ? {type: "period"} : period)], "postprocess": (d) => new Fact(d[0], d[1] ?? [])},
101
+ {"name": "rule$ebnf$1", "symbols": ["arguments"], "postprocess": id},
102
+ {"name": "rule$ebnf$1", "symbols": [], "postprocess": () => null},
103
+ {"name": "rule", "symbols": ["any_atom", "rule$ebnf$1", "_", (PrologLexer.has("colonDash") ? {type: "colonDash"} : colonDash), "_", "body", "_", (PrologLexer.has("period") ? {type: "period"} : period)], "postprocess": (d) => new Rule(d[0], d[1] ?? [], d[5])},
104
+ {"name": "query", "symbols": [(PrologLexer.has("queryOp") ? {type: "queryOp"} : queryOp), "_", "body", "_", (PrologLexer.has("period") ? {type: "period"} : period)], "postprocess": (d) => new Query(d[2])},
105
+ {"name": "body$ebnf$1", "symbols": []},
106
+ {"name": "body$ebnf$1$subexpression$1$subexpression$1", "symbols": [(PrologLexer.has("comma") ? {type: "comma"} : comma)]},
107
+ {"name": "body$ebnf$1$subexpression$1$subexpression$1", "symbols": [(PrologLexer.has("semicolon") ? {type: "semicolon"} : semicolon)]},
108
+ {"name": "body$ebnf$1$subexpression$1", "symbols": ["_", "body$ebnf$1$subexpression$1$subexpression$1", "_", "expression"]},
109
+ {"name": "body$ebnf$1", "symbols": ["body$ebnf$1", "body$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
110
+ {"name": "body", "symbols": ["expression", "body$ebnf$1"], "postprocess": (d) => [d[0], ...d[1].map(x => x[3])]},
111
+ {"name": "expression$subexpression$1", "symbols": ["conditional"]},
112
+ {"name": "expression$subexpression$1", "symbols": ["forall"]},
113
+ {"name": "expression$subexpression$1", "symbols": ["findall"]},
114
+ {"name": "expression$subexpression$1", "symbols": ["unification"]},
115
+ {"name": "expression$subexpression$1", "symbols": ["assignment"]},
116
+ {"name": "expression$subexpression$1", "symbols": ["comparison"]},
117
+ {"name": "expression$subexpression$1", "symbols": ["not"]},
118
+ {"name": "expression$subexpression$1", "symbols": ["exist"]},
119
+ {"name": "expression", "symbols": ["expression$subexpression$1"], "postprocess": (d) => d[0][0]},
120
+ {"name": "expression", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": (d) => d[2]},
121
+ {"name": "conditional", "symbols": [{"literal":"("}, "_", "body", "_", {"literal":"->"}, "_", "body", "_", (PrologLexer.has("semicolon") ? {type: "semicolon"} : semicolon), "_", "body", "_", {"literal":")"}], "postprocess": (d) =>
122
+ new If(
123
+ asSequence(d[2]),
124
+ asSequence(d[6]),
125
+ asSequence(d[10])
126
+ )
127
+ },
128
+ {"name": "forall", "symbols": [(PrologLexer.has("forallRule") ? {type: "forallRule"} : forallRule), {"literal":"("}, "_", "expression", "_", (PrologLexer.has("comma") ? {type: "comma"} : comma), "_", "expression", "_", {"literal":")"}], "postprocess": (d) => new Forall(d[3], d[7])},
129
+ {"name": "findall", "symbols": [(PrologLexer.has("findallRule") ? {type: "findallRule"} : findallRule), {"literal":"("}, "_", "expression", "_", (PrologLexer.has("comma") ? {type: "comma"} : comma), "_", "expression", "_", (PrologLexer.has("comma") ? {type: "comma"} : comma), "_", "expression", "_", {"literal":")"}], "postprocess": (d) => new Findall(d[3], d[7], d[11])},
130
+ {"name": "not", "symbols": [(PrologLexer.has("notOperator") ? {type: "notOperator"} : notOperator), "_", "expression"], "postprocess": (d) => new Not([d[2]])},
131
+ {"name": "not", "symbols": [(PrologLexer.has("notOperator") ? {type: "notOperator"} : notOperator), "_", {"literal":"("}, "_", {"literal":"("}, "_", "body", "_", {"literal":")"}, "_", {"literal":")"}], "postprocess": (d) => new Not([asSequence(d[6])])},
132
+ {"name": "exist", "symbols": [{"literal":"call"}, (PrologLexer.has("lparen") ? {type: "lparen"} : lparen), "_", "pattern_list", "_", (PrologLexer.has("rparen") ? {type: "rparen"} : rparen)], "postprocess": (d) => {
133
+ const [callee, ...rest] = d[3]
134
+ return new Call(callee.name, rest)
135
+ } },
136
+ {"name": "exist$ebnf$1", "symbols": ["arguments"], "postprocess": id},
137
+ {"name": "exist$ebnf$1", "symbols": [], "postprocess": () => null},
138
+ {"name": "exist", "symbols": ["any_atom", "exist$ebnf$1"], "postprocess": (d, l, reject) => {
139
+ const val = d[0].value;
140
+ if (val === "not" || val === "\\+") return reject;
141
+ return new Exist(d[0], d[1] ?? [])
142
+ } },
143
+ {"name": "exist", "symbols": ["variable"], "postprocess": (d) => new Exist(d[0], [])},
144
+ {"name": "assignment", "symbols": ["addition", "_", {"literal":"is"}, "_", "addition"], "postprocess": (d) => new AssignOperation("Assign", d[0], d[4])},
145
+ {"name": "unification", "symbols": ["addition", "_", {"literal":"="}, "_", "addition"], "postprocess": (d) => new UnifyOperation("Unify", d[0], d[4])},
146
+ {"name": "comparison", "symbols": ["addition", "_", "comparison_op", "_", "addition"], "postprocess": (d) => new ComparisonOperation(d[2], d[0], d[4])},
147
+ {"name": "addition", "symbols": ["multiplication", "_", {"literal":"+"}, "_", "addition"], "postprocess": (d) => new ArithmeticBinaryOperation("Plus", d[0], d[4])},
148
+ {"name": "addition", "symbols": ["multiplication", "_", {"literal":"-"}, "_", "addition"], "postprocess": (d) => new ArithmeticBinaryOperation("Minus", d[0], d[4])},
149
+ {"name": "addition", "symbols": ["multiplication"], "postprocess": id},
150
+ {"name": "multiplication", "symbols": ["primary", "_", {"literal":"*"}, "_", "multiplication"], "postprocess": (d) => new ArithmeticBinaryOperation("Multiply", d[0], d[4])},
151
+ {"name": "multiplication", "symbols": ["primary", "_", {"literal":"/"}, "_", "multiplication"], "postprocess": (d) => new ArithmeticBinaryOperation("Divide", d[0], d[4])},
152
+ {"name": "multiplication", "symbols": ["primary"], "postprocess": id},
153
+ {"name": "primary", "symbols": ["arithmetic_literal"], "postprocess": id},
154
+ {"name": "primary", "symbols": ["variable"], "postprocess": id},
155
+ {"name": "primary", "symbols": ["strict_atom", "arguments"], "postprocess": d => new Exist(d[0], d[1] ?? [])},
156
+ {"name": "primary", "symbols": ["primitiveOperation"], "postprocess": id},
157
+ {"name": "primary", "symbols": ["cons_expr"], "postprocess": id},
158
+ {"name": "primary", "symbols": [{"literal":"("}, "_", "addition", "_", {"literal":")"}], "postprocess": d => d[2]},
159
+ {"name": "cons_expr", "symbols": [{"literal":"["}, "_", "primary_list", "_", (PrologLexer.has("consOp") ? {type: "consOp"} : consOp), "_", "expression", "_", {"literal":"]"}], "postprocess": (d) => new ConsExpression(d[2], d[6])},
160
+ {"name": "primitiveOperation", "symbols": [{"literal":"round"}, "__", "addition"], "postprocess": d => new ArithmeticUnaryOperation("Round", d[2])},
161
+ {"name": "primitiveOperation", "symbols": [{"literal":"abs"}, "__", "addition"], "postprocess": d => new ArithmeticUnaryOperation("Absolute", d[2])},
162
+ {"name": "primitiveOperation", "symbols": [{"literal":"sqrt"}, "__", "addition"], "postprocess": d => new ArithmeticUnaryOperation("Sqrt", d[2])},
163
+ {"name": "primitiveArguments", "symbols": [(PrologLexer.has("lparen") ? {type: "lparen"} : lparen), "_", "primary_list", "_", (PrologLexer.has("rparen") ? {type: "rparen"} : rparen)], "postprocess": (d) => d[2]},
164
+ {"name": "primary_list$ebnf$1", "symbols": []},
165
+ {"name": "primary_list$ebnf$1$subexpression$1", "symbols": ["_", (PrologLexer.has("comma") ? {type: "comma"} : comma), "_", "addition"]},
166
+ {"name": "primary_list$ebnf$1", "symbols": ["primary_list$ebnf$1", "primary_list$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
167
+ {"name": "primary_list", "symbols": ["addition", "primary_list$ebnf$1"], "postprocess": (d) => [d[0], ...d[1].map(x => x[3])]},
168
+ {"name": "arguments", "symbols": [(PrologLexer.has("lparen") ? {type: "lparen"} : lparen), "_", "pattern_list", "_", (PrologLexer.has("rparen") ? {type: "rparen"} : rparen)], "postprocess": (d) => d[2]},
169
+ {"name": "pattern_list$ebnf$1", "symbols": []},
170
+ {"name": "pattern_list$ebnf$1$subexpression$1", "symbols": ["_", (PrologLexer.has("comma") ? {type: "comma"} : comma), "_", "pattern"]},
171
+ {"name": "pattern_list$ebnf$1", "symbols": ["pattern_list$ebnf$1", "pattern_list$ebnf$1$subexpression$1"], "postprocess": (d) => d[0].concat([d[1]])},
172
+ {"name": "pattern_list", "symbols": ["pattern", "pattern_list$ebnf$1"], "postprocess": (d) => [d[0], ...d[1].map(x => x[3])]},
173
+ {"name": "pattern", "symbols": ["variable"], "postprocess": (d) => new VariablePattern(d[0])},
174
+ {"name": "pattern", "symbols": ["structural_literal"], "postprocess": (d) => new LiteralPattern(d[0])},
175
+ {"name": "pattern", "symbols": [(PrologLexer.has("wildcard") ? {type: "wildcard"} : wildcard)], "postprocess": (d) => new WildcardPattern()},
176
+ {"name": "pattern", "symbols": ["any_atom", "arguments"], "postprocess": (d) => new FunctorPattern(d[0], d[1])},
177
+ {"name": "pattern", "symbols": [{"literal":"("}, "_", "pattern_list", "_", {"literal":")"}], "postprocess": (d) => new TuplePattern(d[2])},
178
+ {"name": "pattern", "symbols": [{"literal":"["}, "_", "pattern_list", "_", (PrologLexer.has("consOp") ? {type: "consOp"} : consOp), "_", "pattern", "_", {"literal":"]"}], "postprocess": (d) => new ConsPattern(d[2].length > 1 ? new ListPattern(d[2]) : d[2][0], new VariablePattern(d[6]))},
179
+ {"name": "pattern$ebnf$1", "symbols": ["pattern_list"], "postprocess": id},
180
+ {"name": "pattern$ebnf$1", "symbols": [], "postprocess": () => null},
181
+ {"name": "pattern", "symbols": [{"literal":"["}, "_", "pattern$ebnf$1", "_", {"literal":"]"}], "postprocess": (d) => new ListPattern(d[2] ? d[2] : [])},
182
+ {"name": "variable", "symbols": [(PrologLexer.has("variable") ? {type: "variable"} : variable)], "postprocess": (d) => new SymbolPrimitive(d[0].value)},
183
+ {"name": "strict_atom", "symbols": [(PrologLexer.has("atom") ? {type: "atom"} : atom)], "postprocess": (d) => new SymbolPrimitive(d[0].value)},
184
+ {"name": "any_atom$subexpression$1", "symbols": [(PrologLexer.has("atom") ? {type: "atom"} : atom)]},
185
+ {"name": "any_atom$subexpression$1", "symbols": [(PrologLexer.has("op") ? {type: "op"} : op)]},
186
+ {"name": "any_atom$subexpression$1", "symbols": [(PrologLexer.has("comparisonOp") ? {type: "comparisonOp"} : comparisonOp)]},
187
+ {"name": "any_atom", "symbols": ["any_atom$subexpression$1"], "postprocess": (d) => new SymbolPrimitive(d[0][0].value)},
188
+ {"name": "arithmetic_literal", "symbols": ["strict_atom"], "postprocess": id},
189
+ {"name": "arithmetic_literal", "symbols": [(PrologLexer.has("number") ? {type: "number"} : number)], "postprocess": (d) => new NumberPrimitive(Number(d[0].value))},
190
+ {"name": "arithmetic_literal", "symbols": [(PrologLexer.has("string") ? {type: "string"} : string)], "postprocess": (d) => new StringPrimitive(d[0].value)},
191
+ {"name": "structural_literal", "symbols": ["any_atom"], "postprocess": id},
192
+ {"name": "structural_literal", "symbols": [(PrologLexer.has("number") ? {type: "number"} : number)], "postprocess": (d) => new NumberPrimitive(Number(d[0].value))},
193
+ {"name": "structural_literal", "symbols": [(PrologLexer.has("string") ? {type: "string"} : string)], "postprocess": (d) => new StringPrimitive(d[0].value)},
194
+ {"name": "comparison_op", "symbols": [{"literal":"=:="}], "postprocess": d => "Equal"},
195
+ {"name": "comparison_op", "symbols": [{"literal":"=\\="}], "postprocess": d => "NotEqual"},
196
+ {"name": "comparison_op", "symbols": [{"literal":"=="}], "postprocess": d => "Same"},
197
+ {"name": "comparison_op", "symbols": [{"literal":"\\=="}], "postprocess": d => "NotSame"},
198
+ {"name": "comparison_op", "symbols": [{"literal":"\\="}], "postprocess": d => "NotSame"},
199
+ {"name": "comparison_op", "symbols": [{"literal":"@<"}], "postprocess": d => "LessThan"},
200
+ {"name": "comparison_op", "symbols": [{"literal":"@=<"}], "postprocess": d => "LessOrEqualThan"},
201
+ {"name": "comparison_op", "symbols": [{"literal":"@>"}], "postprocess": d => "GreaterThan"},
202
+ {"name": "comparison_op", "symbols": [{"literal":"@>="}], "postprocess": d => "GreaterOrEqualThan"},
203
+ {"name": "comparison_op", "symbols": [{"literal":"<"}], "postprocess": d => "LessThan"},
204
+ {"name": "comparison_op", "symbols": [{"literal":"=<"}], "postprocess": d => "LessOrEqualThan"},
205
+ {"name": "comparison_op", "symbols": [{"literal":">"}], "postprocess": d => "GreaterThan"},
206
+ {"name": "comparison_op", "symbols": [{"literal":">="}], "postprocess": d => "GreaterOrEqualThan"},
207
+ {"name": "comparison_op", "symbols": [{"literal":"=@="}], "postprocess": d => "Same"},
208
+ {"name": "comparison_op", "symbols": [{"literal":"\\=@="}], "postprocess": d => "NotSame"},
209
+ {"name": "_$ebnf$1", "symbols": []},
210
+ {"name": "_$ebnf$1", "symbols": ["_$ebnf$1", (PrologLexer.has("WS") ? {type: "WS"} : WS)], "postprocess": (d) => d[0].concat([d[1]])},
211
+ {"name": "_", "symbols": ["_$ebnf$1"]},
212
+ {"name": "__$ebnf$1", "symbols": [(PrologLexer.has("WS") ? {type: "WS"} : WS)], "postprocess": id},
213
+ {"name": "__$ebnf$1", "symbols": [], "postprocess": () => null},
214
+ {"name": "__", "symbols": ["__$ebnf$1"]}
215
+ ],
216
+ ParserStart: "program",
217
+ };
218
+
219
+ export default grammar;
@@ -0,0 +1,39 @@
1
+ import { makeLexer } from "moo-ignore";
2
+ import moo from "moo";
3
+ export const PrologLexerConfig = {
4
+ WS: /[ \t]+/,
5
+ wildcard: "_",
6
+ notOperator: { match: ["\\+", "not"] },
7
+ forallRule: "forall",
8
+ findallRule: "findall",
9
+ comment: /%.*|\/\*[\s\S]*?\*\//,
10
+ number:
11
+ /-?\b(?:[0-9]+(?:\.[0-9]+(?:e[+-]?[0-9]+)?)?|0o[0-7]+|0x[0-9a-fA-F]+|0b[01]+)\b/,
12
+ string: /(?:'(?:[^']|'')*')/,
13
+ backtick: "`",
14
+ lparen: "(",
15
+ rparen: ")",
16
+ lbracket: "{",
17
+ rbracket: "}",
18
+ lsquare: "[",
19
+ rsquare: "]",
20
+ comma: ",",
21
+ arrow: "->",
22
+ period: ".",
23
+ semicolon: ";",
24
+ colonDash: ":-",
25
+ consOp: "|",
26
+ queryOp: "?-",
27
+ comparisonOp: /@<|@=<|@>=|@>|<|=<|>=|>|=@=|\\=@=|=:=|=\\=|==|\\==|\\=|=/,
28
+ op: /\+|-|\*|\/|\/\/|~|\^|\?|\$|''|\.\./,
29
+ variable: /[A-Z][a-zA-Z0-9_]*/,
30
+ atom: {
31
+ match: /[a-z!][a-zA-Z0-9_]*/,
32
+ type: moo.keywords({
33
+ primitiveOperator: ["round", "abs", "sqrt", "call"],
34
+ }),
35
+ },
36
+ NL: { match: /\r?\n/, lineBreaks: true },
37
+ };
38
+
39
+ export const PrologLexer = makeLexer(PrologLexerConfig, ["comment", "NL"]);
@@ -0,0 +1,35 @@
1
+ import { AST, YukigoParser } from "yukigo-ast";
2
+ import nearley from "nearley";
3
+ import grammar from "./grammar.js";
4
+
5
+ export class YukigoPrologParser implements YukigoParser {
6
+ public errors: string[] = [];
7
+ public parse(code: string): AST {
8
+ const parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
9
+ try {
10
+ parser.feed(code);
11
+ parser.finish();
12
+ } catch (error) {
13
+ console.log(error)
14
+ if ("token" in error) throw Error(error);
15
+ const token = error.token;
16
+ const message = `Unexpected '${token.type}' token '${token.value}' at line ${token.line} col ${token.col}.`;
17
+ this.errors.push(message);
18
+ throw Error(message);
19
+ }
20
+ if (parser.results.length > 1) {
21
+ this.errors.push(
22
+ `Too much ambiguity. ${parser.results.length} ASTs. Output not generated.`
23
+ );
24
+ throw Error(
25
+ `Too much ambiguity. ${parser.results.length} ASTs. Output not generated.`
26
+ );
27
+ }
28
+ if (parser.results.length == 0) {
29
+ this.errors.push("Parser did not generate an AST.");
30
+ throw Error("Parser did not generate an AST.");
31
+ }
32
+
33
+ return parser.results[0];
34
+ }
35
+ }
package/src/std.pl ADDED
@@ -0,0 +1,119 @@
1
+ % between(+Low, +High, ?Value)
2
+ % True if Low =< Value =< High (assuming integers)
3
+ between(Low, High, Low) :- Low =< High.
4
+ between(Low, High, Value) :-
5
+ Low < High,
6
+ NextLow is Low + 1,
7
+ between(NextLow, High, Value).
8
+
9
+ % union(+List1, +List2, -Union)
10
+ % Union of two lists without duplicates (order not preserved)
11
+ union([], L, L).
12
+ union([H|T], L2, Union) :-
13
+ ( member(H, L2)
14
+ -> union(T, L2, Union)
15
+ ; Union = [H|Rest],
16
+ union(T, L2, Rest)
17
+ ).
18
+
19
+ % intersection(+List1, +List2, -Intersection)
20
+ intersection([], _, []).
21
+ intersection([H|T], L2, [H|Rest]) :-
22
+ member(H, L2),
23
+ !,
24
+ intersection(T, L2, Rest).
25
+ intersection([_|T], L2, Rest) :-
26
+ intersection(T, L2, Rest).
27
+
28
+ % max_member(-Max, +List)
29
+ max_member(Max, [Max]) :- !.
30
+ max_member(Max, [H|T]) :-
31
+ max_member(M, T),
32
+ ( H > M
33
+ -> Max = H
34
+ ; Max = M
35
+ ).
36
+
37
+ % min_member(-Min, +List)
38
+ min_member(Min, [Min]) :- !.
39
+ min_member(Min, [H|T]) :-
40
+ min_member(M, T),
41
+ ( H < M
42
+ -> Min = H
43
+ ; Min = M
44
+ ).
45
+
46
+ % sumlist(+List, -Sum)
47
+ sumlist([], 0).
48
+ sumlist([H|T], Sum) :-
49
+ sumlist(T, RestSum),
50
+ Sum is H + RestSum.
51
+
52
+ % length(?List, ?Length)
53
+ length([], 0).
54
+ length([_|Tail], N) :-
55
+ length(Tail, M),
56
+ N is M + 1.
57
+
58
+ % flatten(+NestedList, -FlatList)
59
+ flatten([], []).
60
+ flatten([H|T], Flat) :-
61
+ !,
62
+ flatten(H, FlatH),
63
+ flatten(T, FlatT),
64
+ append(FlatH, FlatT, Flat).
65
+ flatten(X, [X]).
66
+
67
+ % reverse(+List, -Reversed)
68
+ reverse(List, Reversed) :-
69
+ reverse_acc(List, [], Reversed).
70
+
71
+ reverse_acc([], Acc, Acc).
72
+ reverse_acc([H|T], Acc, Reversed) :-
73
+ reverse_acc(T, [H|Acc], Reversed).
74
+
75
+ % list_to_set(+List, -Set)
76
+ % Removes duplicates, keeps first occurrence
77
+ list_to_set([], []).
78
+ list_to_set([H|T], [H|Rest]) :-
79
+ exclude(==(H), T, T1),
80
+ list_to_set(T1, Rest).
81
+
82
+ ==(H, T) :-
83
+ H == T.
84
+
85
+ % nth0(?Index, ?List, ?Elem)
86
+ % Index starts at 0
87
+ nth0(0, [H|_], H).
88
+ nth0(N, [_|T], Elem) :-
89
+ N > 0,
90
+ N1 is N - 1,
91
+ nth0(N1, T, Elem).
92
+
93
+ % nth1(?Index, ?List, ?Elem)
94
+ % Index starts at 1
95
+ nth1(1, [H|_], H).
96
+ nth1(N, [_|T], Elem) :-
97
+ N > 1,
98
+ N1 is N - 1,
99
+ nth1(N1, T, Elem).
100
+
101
+ % append/3 (needed for flatten and union)
102
+ append([], L, L).
103
+ append([H|T], L, [H|R]) :-
104
+ append(T, L, R).
105
+
106
+ % member/2 (needed for many predicates)
107
+ member(H, [H|_]).
108
+ member(H, [_|T]) :-
109
+ member(H, T).
110
+
111
+ % exclude(+Pred, +List, -Filtered)
112
+ % Removes elements satisfying Pred
113
+ exclude(_, [], []).
114
+ exclude(Pred, [H|T], Rest) :-
115
+ ( call(Pred, H)
116
+ -> exclude(Pred, T, Rest)
117
+ ; Rest = [H|Rest2],
118
+ exclude(Pred, T, Rest2)
119
+ ).