diff --git a/topics/exercises/while2/Makefile b/topics/exercises/while2/Makefile
new file mode 100644
index 00000000..010dfcf3
--- /dev/null
+++ b/topics/exercises/while2/Makefile
@@ -0,0 +1,9 @@
+test:
+ swipl -f RunParser.pro -t "main('input.txt')"
+
+debug:
+ cat input.txt
+ swipl -s Parser.pro
+
+clean:
+ rm -rf *~
diff --git a/topics/exercises/while2/Parser.pro b/topics/exercises/while2/Parser.pro
new file mode 100644
index 00000000..85b99d4c
--- /dev/null
+++ b/topics/exercises/while2/Parser.pro
@@ -0,0 +1,142 @@
+%% Parser for While language
+% Program is a list of statements
+program(S) --> statements(S).
+
+% Non-empty list
+statements(slist(H,T)) -->
+ statement(H),
+ keyword(";"),
+ statements(T).
+
+% Empty list
+statements(S) --> statement(S).
+
+%% Statements
+% Skip statement
+statement(skip) -->
+ keyword("skip").
+
+% Assign statement
+statement(assign(identifier(V),E)) -->
+ identifier(V),
+ keyword(":="),
+ aexpression(E).
+
+% Conditional statement
+statement(ifthenelse(E,S1,S2)) -->
+ keyword("if"),
+ bexpression(E),
+ keyword("then"),
+ statement(S1),
+ keyword("else"),
+ statement(S2).
+
+% Loop statement
+statement(while(E,S)) -->
+ keyword("while"),
+ bexpression(E),
+ keyword("do"),
+ statement(S).
+
+
+%% Arithmetic expressions
+% Number is a an arithmetic expression
+aexpression(number(N)) --> number(N).
+
+% Variable reference is a an arithmetic expression
+aexpression(identifier(V)) --> identifier(V).
+
+% A sum of arithmetic expressions is also an arithmetic expression
+aexpression(add(E1,E2)) -->
+ keyword("("),
+ aexpression(E1),
+ keyword("+"),
+ aexpression(E2),
+ keyword(")").
+
+% Same with subtraction
+aexpression(sub(E1,E2)) -->
+ keyword("("),
+ aexpression(E1),
+ keyword("-"),
+ aexpression(E2),
+ keyword(")").
+
+% ...and multiplication
+aexpression(mul(E1,E2)) -->
+ keyword("("),
+ aexpression(E1),
+ keyword("*"),
+ aexpression(E2),
+ keyword(")").
+
+
+%% Boolean expressions
+% Boolean primitives are boolean expressions
+bexpression(true) --> keyword("true").
+bexpression(false) --> keyword("false").
+
+% Comparison is a boolean expression: equality
+bexpression(equals(E1,E2)) -->
+ aexpression(E1),
+ keyword("="),
+ aexpression(E2).
+
+% Comparison is a boolean expression: less than or equal to
+bexpression(lte(E1,E2)) -->
+ aexpression(E1),
+ keyword("≤"),
+ aexpression(E2).
+
+% Negation is a boolean expression
+bexpression(not(E)) -->
+ keyword("¬"),
+ bexpression(E).
+
+% Disjunction of boolean expressions is also a boolean expression
+bexpression(and(E1,E2)) -->
+ keyword("("),
+ bexpression(E1),
+ keyword("^"),
+ bexpression(E2),
+ keyword(")").
+
+
+%% Low-level details
+% Dealing with layout
+layout --> [0' ], layout. %' space
+layout --> [0' ], layout. %' tab
+layout --> [10], layout. % newline
+layout --> [].
+
+% Keywords are space-consuming strings
+keyword(X) -->
+ layout,
+ string(X).
+
+% Bare strings
+string([],X,X).
+string([H|T1],[H|T2],X) :- string(T1,T2,X).
+
+% Numbers are space-consuming digits
+number(N) -->
+ layout,
+ digit(H), digits(T),
+ { number_chars(N,[H|T]) }.
+
+% Bare digits
+digits([H|T]) --> digit(H), digits(T).
+digits([]) --> [].
+digit(H,[H|T],T) :- H >= 0'0, H =< 0'9.
+
+% Identifiers are space-consuming letters
+identifier(V) -->
+ layout,
+ letter(H), letters(T),
+ { atom_chars(V,[H|T]) }.
+
+% Bare letters
+letters([H|T]) --> letter(H), letters(T).
+letters([]) --> [].
+letter(H,[H|T],T) :- H >= 0'a, H =< 0'z.
+
diff --git a/topics/exercises/while2/RunParser.pro b/topics/exercises/while2/RunParser.pro
new file mode 100644
index 00000000..93e8593a
--- /dev/null
+++ b/topics/exercises/while2/RunParser.pro
@@ -0,0 +1,17 @@
+:- ['Parser.pro'].
+
+main(File) :-
+ parseFile(File,program,S),
+ write(S),nl.
+
+parseFile(File,P,R) :-
+ open(File,read,Stream,[]),
+ read_stream_to_codes(Stream, Contents),
+ close(Stream),
+ apply(P,[R,Contents,Rest]),
+ eof(Rest,_).
+
+eof([],[]).
+eof([0' |T],R) :- eof(T,R). %'
+eof([10|T],R) :- eof(T,R).
+
diff --git a/topics/exercises/while2/input.txt b/topics/exercises/while2/input.txt
new file mode 100644
index 00000000..02f7e55c
--- /dev/null
+++ b/topics/exercises/while2/input.txt
@@ -0,0 +1,7 @@
+if ¬x ≤ 0
+ then
+ y:=((2+x)-y)
+ else
+ while z=0 do
+ z:=2;
+skip
diff --git a/topics/exercises/while3/Parser.pro b/topics/exercises/while3/Parser.pro
index 8e916391..85b99d4c 100644
--- a/topics/exercises/while3/Parser.pro
+++ b/topics/exercises/while3/Parser.pro
@@ -1,3 +1,4 @@
+%% Parser for While language
% Program is a list of statements
program(S) --> statements(S).
@@ -10,7 +11,7 @@ statements(slist(H,T)) -->
% Empty list
statements(S) --> statement(S).
-% Statements
+%% Statements
% Skip statement
statement(skip) -->
keyword("skip").
@@ -30,7 +31,15 @@ statement(ifthenelse(E,S1,S2)) -->
keyword("else"),
statement(S2).
-% Expressions
+% Loop statement
+statement(while(E,S)) -->
+ keyword("while"),
+ bexpression(E),
+ keyword("do"),
+ statement(S).
+
+
+%% Arithmetic expressions
% Number is a an arithmetic expression
aexpression(number(N)) --> number(N).
@@ -45,6 +54,24 @@ aexpression(add(E1,E2)) -->
aexpression(E2),
keyword(")").
+% Same with subtraction
+aexpression(sub(E1,E2)) -->
+ keyword("("),
+ aexpression(E1),
+ keyword("-"),
+ aexpression(E2),
+ keyword(")").
+
+% ...and multiplication
+aexpression(mul(E1,E2)) -->
+ keyword("("),
+ aexpression(E1),
+ keyword("*"),
+ aexpression(E2),
+ keyword(")").
+
+
+%% Boolean expressions
% Boolean primitives are boolean expressions
bexpression(true) --> keyword("true").
bexpression(false) --> keyword("false").
@@ -61,13 +88,30 @@ bexpression(lte(E1,E2)) -->
keyword("≤"),
aexpression(E2).
-% Dealing with spaces
-spaces --> [0' ], spaces. %'
-spaces --> [].
+% Negation is a boolean expression
+bexpression(not(E)) -->
+ keyword("¬"),
+ bexpression(E).
+
+% Disjunction of boolean expressions is also a boolean expression
+bexpression(and(E1,E2)) -->
+ keyword("("),
+ bexpression(E1),
+ keyword("^"),
+ bexpression(E2),
+ keyword(")").
+
+
+%% Low-level details
+% Dealing with layout
+layout --> [0' ], layout. %' space
+layout --> [0' ], layout. %' tab
+layout --> [10], layout. % newline
+layout --> [].
% Keywords are space-consuming strings
keyword(X) -->
- spaces,
+ layout,
string(X).
% Bare strings
@@ -76,7 +120,7 @@ string([H|T1],[H|T2],X) :- string(T1,T2,X).
% Numbers are space-consuming digits
number(N) -->
- spaces,
+ layout,
digit(H), digits(T),
{ number_chars(N,[H|T]) }.
@@ -87,7 +131,7 @@ digit(H,[H|T],T) :- H >= 0'0, H =< 0'9.
% Identifiers are space-consuming letters
identifier(V) -->
- spaces,
+ layout,
letter(H), letters(T),
{ atom_chars(V,[H|T]) }.
diff --git a/topics/exercises/xml2/Makefile b/topics/exercises/xml2/Makefile
new file mode 100644
index 00000000..f622b2cc
--- /dev/null
+++ b/topics/exercises/xml2/Makefile
@@ -0,0 +1,12 @@
+test:
+ swipl -f RunParser.pro -t "main('input.txt')"
+
+fail:
+ swipl -f RunParser.pro -t "main('bad.txt')"
+
+debug:
+ cat input.txt
+ swipl -s Parser.pro
+
+clean:
+ rm -rf *~
diff --git a/topics/exercises/xml2/Parser.pro b/topics/exercises/xml2/Parser.pro
new file mode 100644
index 00000000..6ad16895
--- /dev/null
+++ b/topics/exercises/xml2/Parser.pro
@@ -0,0 +1,66 @@
+% A root tree is a regular tree with an opening and closing tags and a forest in between
+tree(tree(N,AL,TL)) -->
+ layout,
+ string("<"),
+ name(N),
+ attrs(AL,_),
+ string(">"),
+ trees(TL),
+ string(""),
+ name(N),
+ string(">"),
+ layout.
+
+% A non-empty forest
+trees([H|T]) -->
+ tree(H),
+ trees(T).
+
+% An empty forest
+trees([]) --> [].
+
+% Attribute list
+attrs([H|T],[N|U]) -->
+ layout,
+ attr(H),
+ attrs(T,U),
+ {
+ H = [N,_],
+ not(member(N,U))
+ }.
+attrs([],[]) --> [].
+
+% Attribute
+attr([N,V]) -->
+ name(N),
+ string("="),
+ value(V).
+
+% Bare strings
+string([],X,X).
+string([H|T1],[H|T2],X) :- string(T1,T2,X).
+
+% Consuming spaces
+layout --> [0' ], layout. % space '
+layout --> [0' ], layout. % tab '
+layout --> [10], layout. % newline
+layout --> [].
+
+% Only lowcased tag names are allowed
+name(N) -->
+ letter(H), letters(T),
+ { atom_codes(N,[H|T]) }.
+
+% Bare letters
+letters([H|T]) --> letter(H), letters(T).
+letters([]) --> [].
+letter(H,[H|T],T) :- H >= 0'a, H =< 0'z.
+
+value(V) -->
+ char(H), chars(T),
+ { atom_codes(V,[H|T]) }.
+
+chars([H|T]) --> char(H), chars(T).
+chars([]) --> [].
+char(H,[H|T],T) :- H >= 0'a, H =< 0'z.
+char(H,[H|T],T) :- H >= 0'0, H =< 0'9.
diff --git a/topics/exercises/xml2/RunParser.pro b/topics/exercises/xml2/RunParser.pro
new file mode 100644
index 00000000..96dbf54c
--- /dev/null
+++ b/topics/exercises/xml2/RunParser.pro
@@ -0,0 +1,19 @@
+:- ['Parser.pro'].
+
+main(File)
+ :-
+ parseFile(File,tree,S),
+ write(S), nl.
+
+parseFile(File,P,R)
+ :-
+ open(File,read,Stream,[]),
+ read_stream_to_codes(Stream, Contents),
+ close(Stream),
+ apply(P,[R,Contents,Rest]),
+ eof(Rest,_).
+
+eof([],[]).
+eof([0' |T],R) :- eof(T,R). %'
+eof([10|T],R) :- eof(T,R).
+
diff --git a/topics/exercises/xml2/bad.txt b/topics/exercises/xml2/bad.txt
new file mode 100644
index 00000000..c35061b8
--- /dev/null
+++ b/topics/exercises/xml2/bad.txt
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/topics/exercises/xml2/input.txt b/topics/exercises/xml2/input.txt
new file mode 100644
index 00000000..75c2eae5
--- /dev/null
+++ b/topics/exercises/xml2/input.txt
@@ -0,0 +1,4 @@
+
+
+
+