% version 1.1., 2003-07-11 /* Copyright (C) Wolfgang Menzel, Universität Hamburg, 2003-07-09 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ % a simple tutoring system shell using a grammar-based % diagnosis % errors are described using a special format, e.g. % agree(Phrase1,Phrase2,Feature,Value1,Value2) % descriptions for other types of errors need to be included yet :-module(grammar_diag,[do_exercise/1,get_prompt/2,next_item/4,check_utt/6, get_sols/3]). %%% reading an utterance from the terminal / from an atom % read_utt(-Utterance) % read an utterance from the input, terminated by a return % all characters after the first full stop are ignored read_utt(Utt) :- read_line_to_codes(user,Line), % Read an input line get_u(Line,[],U), % Transform the input into a list of words reverse(U,Utt). % get_utt(+Text,-Utterance) % extract an utterance from the input string as delivered by the browser get_utt(Text,List) :- atom_codes(Text,Codes), % convert the input to a list of ascii values get_u(Codes,[],ListR), % extract the words reverse(ListR,List). % get_u(+InputLine,+AlreadyCollectedWords,-ListOfWords) % Transform an input line into a list of words (in reverse order % to be called with an empty list on second argument get_u([],Acc,Acc) :- !. % the input line is exhausted get_u(Line,Acc,Utt) :- get_w(Line,[],Word,RestLine), % get the first word (Word = '' -> % if it is empty get_u(RestLine,Acc,Utt) ; % ignore it get_u(RestLine,[Word|Acc],Utt)). % otherwise keep it in the accumulator % get_w(+InputLine,+AlreadyCollectedCharacters,-CompleteWord,-RemainingInput) % collect a word from the beginning of the input line % and return it together with the remainder of the input line get_w([],Acc,Word,[]) :- % the input line is exhausted reverse(Acc,Chars), % return the last word atom_chars(Word,Chars). get_w([C|RestLine],Acc,Word,RestLine) :- del(C),!, % the first character is a word delimiter reverse(Acc,Chars), % return the current word atom_chars(Word,Chars). get_w([C|_],Acc,Word,[]) :- end(C),!, % the first character is a fullstop reverse(Acc,Chars), % return the last word atom_chars(Word,Chars). get_w([C|RestLine],Acc,Word,RLine) :- ( (C>=65,C=<90) -> C1 is C+32 ; C1=C), % upper case to lower case get_w(RestLine,[C1|Acc],Word,RLine). % collect the characters of a word %%% exercise control (main loop) for interactive use % do_exercise (+NameOfExercise) % cycles through all the items of an exercise do_exercise(Name) :- exercise(Name,Prompt), % find the prompt format('~w~n',[Prompt]), % and write it on the terminal item(Name,_,Task), % find an item tab(3),write_ln(Task), % and write its task read_utt(Utt), % read the utterance from the student feedback(Utt), nl, fail. % iterate over all items do_exercise(_) :- write_ln('End of exercise. Bye.'). feedback(Utt) :- s(Utt,_,_,[]), !, format('well done! ~n'). feedback(Utt) :- s(Utt,_,_,Errors), !, member(E,Errors), put_message(E), read_utt(Utt), (Utt=[] -> fail ; (!, feedback(Utt))). feedback(_) :- Message='Sorry, that was not what I expected', format('~w~n',[Message]), % write explanation read_utt(Utt), (Utt=[] -> fail ; (!, feedback(Utt))). put_message(agree(C1,C2,Feature,Value1,Value2)) :- word(C1,W1), word(C2,W2), format('~w and ~w do not agree in ~w (~w instead of ~w)~n', [W1,W2,Feature,Value1,Value2]). del(32). % space is a word delimiter end(46). % fulstop is a sentence delimiter %%% primitives for web-access % get_prompt(+Exercise,-Prompt) % get the prompt from the database get_prompt(Exercise,Prompt) :- exercise(Exercise,Prompt). % next_item(+Exercise,+ItemId,-Text,-NewItemId) % supply the next exercise item and return the ItemId increased by one next_item(Exercise,ItemId,Text,NewItemId) :- atom_to_number(ItemId,IdNumber), % convert the item number IdNumber1 is IdNumber+1, % increment it item(Exercise,IdNumber1,Text), % fetch the text number_to_atom(IdNumber1,NewItemId). % convert the increased number % check_utt(+Exercise,+ItemId,ExLevel,+Text,-Reply,-NewExLevel) % generate a feedback (Reply) to the student input (Text) % and increase the explanation level by one % ExLevel = 11111: last explanation available (no button "more information" % ExLevel = 22222: no more explanations available (make a correction proposal) % ExLevel = 33333: well done (no more information, no correction) check_utt(grammar,_,ExLevel,Text,Reply,NewExLevel) :- get_utt(Text,List), % get the student input as list atom_number(ExLevel,ExLevelNumber), s(List,_,_,Errors), !, get_explanation(ExLevelNumber,Errors,Reply,NewExLevel). check_utt(_,_,_,_,'Sorry, that was not what I expected!','11111'). get_explanation(_,[],'well done!','33333') :- !. get_explanation(ExLevelNumber,Errors,Reply,NewExLevel) :- ExLevel1 is ExLevelNumber + 1, length(Errors,NoExpl), (ExLevel1>NoExpl -> NewExLevel='11111' ; (nth1(ExLevel1,Errors,E), gen_message(E,Reply), (NoExpl=:=ExLevel1 -> NewExLevel='11111' ; number_to_atom(ExLevel1,NewExLevel)))). gen_message(government(C1,C2,Feature,Value1,Value2),Message) :- !, word(Value1,V1), word(C2,W2), concat_atom([W2, 'has to be in', V1, Feature, ', but it is not.'], ' ',Message). gen_message(agree(C1,C2,Feature,Value1,Value2),Message) :- !, word(C1,W1), word(C2,W2), concat_atom([W1, and, W2, 'do not agree in', Feature, '(', Value1, 'instead of', Value2, ').'], ' ',Message). gen_message(lp(C1,C2),Message) :- !, word(C1,W1), word(C2,W2), concat_atom([W1, and, W2, 'appear in the wrong order.'], ' ',Message). gen_message(X,X). word(vp(_),'verb phrase') :- !. word(np(_),'noun phrase') :- !. word(vp(_,_),'verb phrase') :- !. word(np(_,_),'noun phrase') :- !. word(dir_obj,'the direct object') :- !. word(indir_obj,'the indirect object') :- !. word(dat,dative) :- !. word(X,X). get_sols(grammer,_,'no solution proposal available'). % perhaps a more sophisticated solution is required % atom_to_number(+Atom,?Number) atom_to_number(Atom,Number) :- atom_chars(Atom,AtomList), number_chars(Number,AtomList). % number_to_atom(+Number,?Atom) number_to_atom(Number,Atom) :- number_chars(Number,AtomList), atom_chars(Atom,AtomList). %%% To DO %%%%% % several (three ?) attempts to produce a good solution % several explanations % alternative explanations % different explanation levels (including corrections) % all this will require a different feedback representation in the DB % pictures in the item-text