~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Generating List Mapping Predicates program_list_map( Predicate_name, Element_predicate) :- % build the head for the null list rule Null_list_rule_head =.. [ Predicate_name, [] , [] ], % assert the null list rule, including a cut asserta(( Null_list_rule_head :- !)), % build recursive rule head Recursive_rule_head =.. [ Predicate_name, [H | T] , [H1 | T1] ], % build call for Element_predicate Element_predicate_call =.. [ Element_predicate , H, H1 ], % build recursive call Recursive_call =.. [ Predicate_name , T, T1 ], % assert recursive rule assertz( ( Recursive_rule_head :- Element_predicate_call, Recursive_call )). Box 1 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Program Scheme with Comments $ /* Predicate_name User_defined_purpose */ /* Predicate_name maps null set into null set */ Predicate_name( [], []) :- !. /* recursive rule for Predicate_name */ Predicate_name( [H|T], [H1|T1]) :- /* apply Element_predicate to head of list */ Element_predicate( H, H1), /* recurse with Predicate_name on tail of list */ Predicate_name(T, T1). $, Box 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Information in a Programmer's Scheme [comment([$/*$, var($Predicate_name$), var($User_defined_purpose$), $*/$]), comment([$/*$, var($Predicate_name$), $maps$, $null$, $set$, $into$, $null$, $set$, $*/$]), rule((var_functor_term(var($Predicate_name$),[[],[]]) :- [$!$])), comment([$/*$,$recursive$,$rule$,$for$,var($Predicate_name$),$*/$]), rule((var_functor_term(var($Predicate_name$), [[var($H$) | var($T$)], [var($H1$) | var($T1$)]]) :- [ comment([$/*$, $apply$, var($Element_predicate$), $to$, $head$, $of$, $list$, $*/$]), var_functor_term(var($Predicate_name$), [var($H1$) , var($H1$)]), comment([$/*$, $recurse$, $with$, var($Predicate_name$), $*/$]), var_functor_term(var($Predicate_name$), [var($T1$) , var($T1$)]) ]))] Box 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A BNF for Prolog Program Schemes % a schema definition is a list of items (clauses or comments) scheme_def --> item | scheme_def | [] % an item is a clause or comment item --> clause | comment % an clause is a fact or rule clause --> fact | rule % a fact is a term followed by a period fact --> term . % a rule is a term (the head) followed by the neck symbol % followed by a (rule) body followed by a period rule --> term :- body . % a body is a comment followed by body % or a term followed by a comma followed by a body % or a term or a comment body --> comment body | term , body | term | comment % a term is a functor symbol followed by an argument list % or a set or a constant or a variable term --> functor_symbol arg_list | set | constant | variable % an arg_list is a term_list in parens arg_list --> ( termlist ) % a term_list is a term followed by a ter_list or a term term_list --> term term_list | term % a functor symbol is an atom or variable functor_symbol --> atom | variable % a set is a list of terms or the empty list set --> [ set_termlist | [] % a termlist is a term followed by a comma followed by a termlist % or a term followed by a right bracket % or a term, a bar, a term, and a right bracket set_termlist --> term, set_termlist | term ] | term bar term ] % def. of bar bar --> | % a comment is a comment starter followed by a (comment) % word list comment --> start_comment word_list % a word_list is a word followed by a word_list % or an end of comment word_list --> word word_list | end_comment % a word is a variable or a token word --> variable | token start_comment --> /* end_comment --> */ Box 5 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Definite Clause Grammar for Schemes scheme_def(Scheme) --> item(H), scheme_def( T), {Scheme= [ H | T], p_trace($Scheme : $, Scheme)},!. scheme_def([],[],[]) :- p_trace($Scheme = [] $),!. item(X) --> fact(X) , !,{p_trace($item : $, X)}. item(X) --> rule(X) , !,{p_trace($item : $, X)}. item(X) --> comment(X) , !,{p_trace($item : $, X)}. fact(fact(Fact)) --> term( Fact), [$.$], {p_trace($Fact : $,Fact)}. rule(Rule) --> term(Head), [$:-$], {p_trace($starting rule body$)}, body(Body), {Rule = rule((Head :- Body)), p_trace($Rule : $,Rule)}. body( Body ) --> comment(H), body(T),!, { Body = [H | T], p_trace($Body : $,Body)}. body( Body ) --> term(H), [$,$], body(T),!, { Body = [H | T], p_trace($Body : $,Body)}. body( [Term]) --> term( Term), [$.$],!, {p_trace($Body : $, Term)}. body( [Comment]) --> comment(Comment), [$.$],!, {p_trace($Body : $, Comment)}. % a term is a functor symbol followed by an argument list % or a set or a constant or a variable or a set term(Term) --> variable(Variable), [$($], {p_trace($entering arg_list $)}, arg_list(Arg_list), !, {Term = var_functor_term( Variable, Arg_list), p_trace($term: $,Term)}. term(Term) --> is_atom(X), [$($], arg_list(Arg_list), !, {Term = const_functor_term( X, Arg_list), p_trace($term: $,Term)}. term(X) --> set(X), ! , { p_trace($term: $,X)}. term(X) --> is_atomic(X), ! , { p_trace($term: $,X)}. term(X) --> variable(X), ! , { p_trace($term: $,X)}. arg_list(Arglist = [Term | Termlist]) --> term(Term) , arg_list_hlpr(Termlist),!, { Arglist = [Term | Termlist], p_trace($arg_list: $,Arglist)}. arg_list_hlpr([]) --> [$)$] , !, { p_trace($arg_list_hlpr: []$)}. arg_list_hlpr(Termlist) --> [$,$] , arg_list( Termlist) , !, { p_trace($arg_list_hlpr: $, Termlist )}. % set --> [ termlist set( Set ) --> [$[$], termlist(Set),!,{p_trace($Set : $,Set)}. % set --> [ ] set( Set ) --> [$[$,$]$],{Set = [], p_trace($Set : $,Set)}. termlist(Termlist) --> term(H), termlist_hlpr(T), { Termlist = [H | T], p_trace($termlist : $, Termlist)}. termlist_hlpr([]) --> [$]$],!,{p_trace($termlist : []$)}. termlist_hlpr(T) --> [$|$], term(T),[$]$],!, {p_trace($termlist : $, T)}. termlist_hlpr(T) --> comma($,$), termlist(T), !, {p_trace($termlist : $, T)}. comment( Comment ) --> start_comment( H), word_list(T), { Comment = comment([H | T]), p_trace($Comment : $,Comment) }. % the straightforward implementation, like that of % start_comment, did not work properly end_comment($*/$) --> [$*/$]. start_comment($/*$) --> [$/*$]. % word_list --> word word_list | end_comment word_list( [H | T] ) --> word(H), word_list( T ), !. word_list( [H] ) --> end_comment( H ). % word --> variable | token word(X) --> variable(X),!. % don't let an end of comment be a word word(X) --> end_comment(X), !, {fail}. word(X) --> token(X). % returns a variable inside a var(*) marker variable(var(X)) --> [X], % get the next token % get its first character {nth_char(0,X,Char), % see if it's upper case is_uc(Char)}. % get an atom from input stream is_atom(X) --> [X], % get the next token % get its first character {nth_char(0,X,Char), % see if it's lower case is_lc(Char)}. % get an atomic structure from input stream is_atomic(X) --> [X], % get the next token % see if it's atomic {atomic(X)},!. comma(X) --> [$,$],!. % returns an arbitrary token as itself token(X) --> [X],!. Box 6 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A User-Supplied Scheme Fact scheme( program_list_map, $ /* Predicate_name User_defined_purpose */ ....................... < complete scheme from Box 2 goes here> ....................... Predicate_name(T, T1). $, [ $Predicate_name$ : $name of predicate to be defined$, $User_defined_purpose$ : $description of what the predicate does$, $Element_predicate$ : $predicate that maps set elements$]). Box 7 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Converting a Scheme to Internal Form preprocess( scheme( Name, Scheme, Table), internal_scheme( Name, Internal_Scheme, Table) :- % tokenize the input tokenize(Scheme, Tokens), % build scheme semantic structure scheme_def( Internal_Scheme, Tokens, []). Box 8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Top Level of Code Generation % generate code from a scheme given its name process( Scheme_name) :- % look up internal scheme internal_scheme( Name, Internal_Scheme, Table), % get the values of variables in Table, get_variable_values(Table, Values), % get temporary file for code temporary_file_handle(Temp_file_name, Temp_file_handle), % get the file name where the code should go permanently get_destination_file(Peramanent_filename), % generate the code generate_code(Temp_file_handle, Values, Internal_Scheme), % close temp file close( Temp_file_handle ), % append code to the permanent file append_files( Temp_file_name, Peramanent_filename). % get the values of variables in a scheme Table get_variable_values(Table, Values) :- % get Table in reverse order to make questions % come out in forward order during recursion reverse(Table, Reversed_table), % ask questions and build frame of variable values ask_questions( Reversed_table, Values0), % put attribautes back in original order reverse(Values0, Values). % ask questions and build frame of variable values ask_questions( [], [] ) :- !. ask_questions( [H | T], [H1 | T1] ) :- ask_question(H , H1), ask_questions(T , T1). % ask the user a question ask_question( Variable : Question, Variable : Value) :- % write question to screen write( Question),write($ ? $), % read user response read_line(1, Value). % append File2 to File1 append_files( File1, File2) :- % build a DOS copy command that appends files concat([$copy $,File1,$+$,File2,$,$,File1],Cmd), % send it to DOS shell(Cmd). Box 10 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Code Generation % generate the output code. Scheme is input stream. % This is written as a Prolog predicate instead of % a grammar rule because of a suspected bug in the DCG % translator generate_code_hlpr(Temp_file_handle, Values, % does the input start with a comment [comment(X) | Rest], Left_over) :- o_trace($e generate_code_hlpr$), % generate initial comment with 0 indent generate_comment( Temp_file_handle, Values, 0, [comment(X)],[]),!, % blank line after this comment nl(Temp_file_handle), o_trace($a initial comment$), % generate each clause in turn generate_clauses( Temp_file_handle, Values, Rest, Left_over). generate_code_hlpr(Temp_file_handle, Values) --> % otherwise just generate clauses generate_clauses( Temp_file_handle, Values). generate_comment( Handle, Values, Offset) --> {o_trace($e generate_comment$)}, [comment([$/*$ | X])], { nl(Handle), tab(Handle,Offset), write(Handle, $/*$), generate_comment_hlpr( Handle, Values, X, [])},!. generate_comment_hlpr( Handle, Values) --> [$*/$],{write_spaced_token(Handle,$*/$)},!. generate_comment_hlpr( Handle, Values) --> scheme_token( Values, Value), {write_spaced_token(Handle, Value)}, generate_comment_hlpr( Handle, Values). write_spaced_token(Handle, Value) :- tab(Handle, 1), write(Handle, Value). scheme_token( Values, Value) --> [var(Indicator)],!, {get_frame_value_with_default(Values, Indicator, Indicator, Value), o_trace($Token = $,Value)}. scheme_token( Values, Value) --> [Value], {o_trace($Token = $, Value)}. %%%%%%%%%%%%%%%%%%% tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% test :- generate_code(1, % put output to screen for test % Here are the values of the template variables [$Predicate_name$ : list_of_types, $User_defined_purpose$ : $maps a list into a list of types elementwise$, $Element_predicate$ : type], % Here is the processed template [comment([$/*$, var($Predicate_name$), var($User_defined_purpose$), $*/$]), comment([$/*$, var($Predicate_name$), $maps$, $null$, $set$, $into$, $null$, $set$, $*/$]), rule((var_functor_term(var($Predicate_name$),[[],[]]) :- [$!$])), comment([$/*$,$recursive$,$rule$,$for$,var($Predicate_name$),$*/$]), rule((var_functor_term(var($Predicate_name$), [[var($H$) | var($T$)], [var($H1$) | var($T1$)]]) :- [ comment([$/*$, $apply$, var($Element_predicate$), $to$, $head$, $of$, $list$, $*/$]), var_functor_term(var($Element_predicate$), [var($H1$) , var($H1$)]), comment([$/*$, $recurse$, $with$, var($Predicate_name$), $*/$]), var_functor_term(var($Predicate_name$), [var($T1$) , var($T1$)]) ]))] ). Box 11 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Retrieval from Frames /* This predicate gets a Value from a Frame given an Indicator (slot name). It gets the Value stored in the frame if there is one, and Default otherwise. */ % rule after frame is searched get_frame_value_with_default( [], _, Default, Default) :-!. % rule for when the value is in the first pair get_frame_value_with_default( [Indicator : Value | _], Indicator, _, Value):- !. % recursive rule get_frame_value_with_default( [_|T], Indicator, Default, Value) :- get_frame_value_with_default( T, Indicator, Default, Value). Box 12 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Test of the Code Generator /* Here is the test: the box 2 template with some scheme variable values. */ test :- generate_code(1, % put output to screen for test % Here are the values of the template variables [$Predicate_name$ : list_of_types, $User_defined_purpose$ : $maps a list into a list of types elementwise$, $Element_predicate$ : type], % Here is the processed template [comment([$/*$, var($Predicate_name$), var($User_defined_purpose$), $*/$]), comment([$/*$, var($Predicate_name$), $maps$, $null$, $set$, $into$, $null$, $set$, $*/$]), rule((var_functor_term(var($Predicate_name$),[[],[]]) :- [$!$])), comment([$/*$,$recursive$,$rule$,$for$,var($Predicate_name$),$*/$]), rule((var_functor_term(var($Predicate_name$), [[var($H$) | var($T$)], [var($H1$) | var($T1$)]]) :- [ comment([$/*$, $apply$, var($Element_predicate$), $to$, $head$, $of$, $list$, $*/$]), var_functor_term(var($Element_predicate$), [var($H1$) , var($H1$)]), comment([$/*$, $recurse$, $with$, var($Predicate_name$), $*/$]), var_functor_term(var($Predicate_name$), [var($T1$) , var($T1$)]) ]))] ). /* Here is the generated code: */ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /* list_of_types maps a list into a list of types elementwise */ /* list_of_types maps null set into null set */ list_of_types([], []) :- !. /* recursive rule for list_of_types */ list_of_types([H | T], [H1 | T1]) :- /* apply type to head of list */ type(H1, H1), /* recurse with list_of_types */ list_of_types(T1, T1). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Box 13 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A Scheme for Selecting a Scheme internal_scheme( sort, [$memory_size$ : $how much memory do you have$, $sort_size$ : $how many items do you have to sort$, $item_size$ : $how big is each item$], sort_helper). sort_helper( Variables) :- get_frame_value(Variables, $memory_size$, Memory), get_frame_value(Variables, $sort_size$, Items), get_frame_value(Variables, $item_size$, Size), select_sort( Memory, Items, Size). select_sort( Memory, Items, Size) :- Size < 20, process(insert_sort). select_sort( Memory, Items, Size) :- Required is Items * Size + 10E5, Memory > Required, process(quick_sort). select_sort( _, _, _) :- process(merge_sort). Box 14 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  process(merge_sort). Box 14 ~~~~~~~~~~~~~~~~~~~~~~