--- title: "Natural Language" type: "article" slug: "natural-language" url: "http://localhost/article/natural-language/" markdown_url: "http://localhost/article/natural-language.md" published_at: "2025-12-18T14:45:53+00:00" modified_at: "2026-06-21T23:20:50+00:00" featured_image: url: "http://localhost/wp-content/uploads/2022/04/20230809-041332.jpg" excerpt: "A while back I asked for some help in porting a program listed in the C Users Journal. Emiliano Barbaini of Italy volunteered to help. I sent him the code and in return he sent me back the changes he made to get the program to run on the QL and on an BULL DPX/2.…" category: - name: "QL Hacker's Journal" slug: "ql-hackers-journal" taxonomy: "category" url: "http://localhost/category/periodicals/ql-hackers-journal/" post_tag: - name: "C (programming language)" slug: "c-programming-language" taxonomy: "post_tag" url: "http://localhost/tag/c-programming-language/" - name: "Full Text" slug: "fulltext" taxonomy: "post_tag" url: "http://localhost/tag/fulltext/" - name: "QL" slug: "ql" taxonomy: "post_tag" url: "http://localhost/tag/ql/" model: - name: "Sinclair QL" slug: "sinclair-ql" taxonomy: "model" url: "http://localhost/model/sinclair-ql/" indiv: - name: "Tim Swenson" slug: "tim-swenson" taxonomy: "indiv" url: "http://localhost/indiv/tim-swenson/" publication_r: id: 33686 title: "QL Hacker’s Journal" type: "periodical" url: "http://localhost/periodical/ql-hackers-journal/" authors_r: - name: "Tim Swenson" slug: "tim-swenson" taxonomy: "indiv" url: "http://localhost/indiv/tim-swenson/" issue: "18" issues_articles: - id: 61554 title: "QL Hacker’s Journal 18" type: "issue" url: "http://localhost/issue/ql-hackers-journal-18/" pubdate: "August 1994" archive_link: true --- # Natural Language A while back I asked for some help in porting a program listed in the C Users Journal. Emiliano Barbaini of Italy volunteered to help. I sent him the code and in return he sent me back the changes he made to get the program to run on the QL and on an BULL DPX/2. I took the changes, made them, and then compiled the program. It compiled fine, but I did not notice any changes to the output (still did not work). Emiliano used C68 v 3.05 where as I used v 3.03 (Hey, I know 3.14c is the latest, just shows how often I use C68). This might have made the difference. I want not too sure about presenting the code here, but Emiliano was able to get it running on a QL. So, I’m sure it’s just something that I am doing (or not doing). Give it a try and see how it works for you. There were some follow on articles in the C Users Journal that expanded this Natural Language program. If you are interested, I can photocopy the articles for you. The source code for recent issues of the CUJ are available on the Internet. ``` /* natural_c */ #include #include #include #include #define ING 73 /* Restriction for ING word */ void initialize(void); void reset_sentence(void); void get_record(char *); char *extract_word(void); int match_record(char *, int); char *extract_root(void); void check_underlying(void); int check_type(char *,int); void check_subject(void); void check_action(void); void check_place(void); void make_response(void); void make_answer(int); void get_verb_ing(void); int match_verb_ing(void); FILE *infile; char dic_record[80]; int sentence; int word_ct; char word_array[10][15]; char root_array[10][15]; char prime_types[10][11]; char phrases[10][11]; char type_array[10][5][11]; char subjects[20][15]; char actions[20][15]; char places[20][31]; char response[80]; void main() { char *cur_word; char in_sentence[80]; initialize(); if ((infile = fopen("diction", "r+")) == NULL) { printf ("¥nError opening dictionary¥n"); exit(0); } printf("¥nSentence: "); while(gets(in_sentence)) { if (in_sentence[0] == '¥0') break; reset_sentence(); cur_word = strtok(in_sentence, " "); while(cur_word != NULL) { get_record(cur_word); cur_word = strtok(NULL, " "); if (++word_ct > 9) break; } check_underlying(); check_subject(); check_action(); check_place(); make_response(); printf("Response: %s¥n¥nSentence: ", response); if (++sentence > 19) break; } /* end while */ fclose(infile); return; } /*****************************************************/ /* Initialize variables (subjects, actions and */ /* places arrays contain entries for 20 sentences). */ /*****************************************************/ void initialize() { int i; for (i=0; i<20; i++) { subjects[i][0] = '¥0'; actions[i][0] = '¥0'; places[i][0] = '¥0'; } sentence = 0; return; } /*****************************************************/ /* These variables are initialized for each new */ /* input sentence (each of the 10 word entries for */ /* the input sentence has 5 type_array entries). */ /*****************************************************/ void reset_sentence() { int i,j; word_ct = 0; for (i=0; i<10; i++) { word_array[i][0] = '¥0'; root_array[i][0] = '¥0'; prime_types[i][0] = '¥0'; phrases[i][0] = '¥0'; for (j=0; j<5; j++) type_array[i][j][0] = '¥0'; } return; } /*****************************************************/ /* Get all the records from the dictionary. If the */ /* passed word is not in the dictionary, then the */ /* word could be a name. */ /*****************************************************/ void get_record(char *pass_word) { int types = 0; rewind (infile); fgets(dic_record, 80, infile); while (! feof(infile)) { if (match_record(pass_word, types) == 0) types++; fgets(dic_record, 80, infile); /***** Deleted Line puts(dic_record); *********/ } if (types == 0) { /*** Old Line ***/ if (isupper( (int) pass_word[0])) /**** ***/ if (isupper(pass_word[0]) != 0) strcpy(type_array[word_ct][types], "NAME"); else strcpy(type_array[word_ct][types], "NOTFOUND"); } strcpy(word_array[word_ct], pass_word); return; } /*****************************************************/ /* Compare the passed word with the word in the */ /* current dictionary record. If they are the same, */ /* then extract the type (NOUN, VERB, etc.). If the */ /* type is a VERB, then also extract the root and */ /* and copy it to the root array. */ /*****************************************************/ int match_record(char *pass_word, int types) { int i, j; char *root; char *dic_word; dic_word = extract_word(); /* Check if passed word equals dictionary word */ if (strcmpi(pass_word, dic_word) != 0) return(1); /* Word found, get the type */ for (i=14,j=0; i<20; i++) { if (isspace(dic_record[i])) break; type_array[word_ct][types][j++] = dic_record[i]; } /* Trim the type */ type_array[word_ct][types][j] = '¥0'; if (strcmp(type_array[word_ct][types], "VERB") == 0) { root = extract_root(); strcpy(root_array[word_ct], root); } return(0); } /*****************************************************/ /* Extract the word from the dictionary. The word is */ /* 14 characters in length and starts in column 1. */ /*****************************************************/ char *extract_word() { int i, j; char dic_word[15]; for (i=0,j=0; i<14; i++) { if (isspace(dic_record[i])) break; dic_word[j++] = dic_record[i]; } /* Trim the dictionary word */ dic_word[j] = '¥0'; return(dic_word); } /*****************************************************/ /* Extract the root from the dictionary. It */ /* identifies a group of similar words (the root for */ /* run, ran, runs and running is run). It is 14 */ /* characters in length and starts in column 35. */ /*****************************************************/ char *extract_root() { int i, j; char root[15]; for (i=34,j=0; i<48; i++) { if (isspace(dic_record[i])) break; root[j++] = dic_record[i]; } /* Trim the root */ root[j] = '¥0'; return(root); } /*****************************************************/ /* Determine if the input sentence contains a known, */ /* underlying structure. If it does, then assign the */ /* correct types and phrases for the words. */ /*****************************************************/ void check_underlying() { int i; /* Structure WH-AUX-NAME-VERB */ i = 0; if ( (check_type("WH", i) == 0) && (check_type("AUX", i+1) == 0) && (check_type("NAME", i+2) == 0) && (check_type("VERB", i+3) == 0) ) { strcpy(prime_types[i], "WH"); strcpy(prime_types[i+1], "AUX"); strcpy(prime_types[i+2], "NAME"); strcpy(prime_types[i+3], "VERB"); strcpy(phrases[i], "WHQUESTION"); strcpy(phrases[i+1], "VERBPHRASE"); strcpy(phrases[i+2], "NOUNPHRASE"); strcpy(phrases[i+3], "VERBPHRASE"); return; } /* Structure WH-AUX-VERB-NAME *** NEW **** */ i = 0; if ( (check_type("WH", i) == 0) && (check_type("AUX", i+1) == 0) && (check_type("VERB", i+2) == 0) && (check_type("NAME", i+3) == 0) ) { strcpy(prime_types[i], "WH"); strcpy(prime_types[i+1], "AUX"); strcpy(prime_types[i+2], "VERB"); strcpy(prime_types[i+3], "NAME"); strcpy(phrases[i], "WHQUESTION"); strcpy(phrases[i+1], "VERBPHRASE"); strcpy(phrases[i+2], "VERBPHRASE"); strcpy(phrases[i+3], "NOUNPHRASE"); return; } /* Structure NAME-AUX-VERB-PREP-DET-NOUN */ if ( (check_type("NAME", i) == 0) && (check_type("AUX", i+1) == 0) && (check_type("VERB", i+2) == 0) && (check_type("PREP", i+3) == 0) && (check_type("DET", i+4) == 0) && (check_type("NOUN", i+5) == 0) ) { strcpy(prime_types[i], "NAME"); strcpy(prime_types[i+1], "AUX"); strcpy(prime_types[i+2], "VERB"); strcpy(prime_types[i+3], "PREP"); strcpy(prime_types[i+4], "DET"); strcpy(prime_types[i+5], "NOUN"); strcpy(phrases[i], "NOUNPHRASE"); strcpy(phrases[i+1], "VERBPHRASE"); strcpy(phrases[i+2], "VERBPHRASE"); strcpy(phrases[i+3], "PREPPHRASE"); strcpy(phrases[i+4], "PREPPHRASE"); strcpy(phrases[i+5], "PREPPHRASE"); return; } return; } /*****************************************************/ /* Compare the passed type with all the types for */ /* this word in the type_array. If the type is */ /* found, then return 0. The pass_number parameter */ /* identifies the word in the input sentence. */ /*****************************************************/ int check_type(char *pass_type, int pass_number) { int i; for (i=0; type_array[pass_number][i][0]; i++) { if (strcmp(type_array[pass_number][i], pass_type) == 0) /* Passed type is found in array */ return(0); } /* Passed type is not found in array */ return(1); } /*****************************************************/ /* If the correct type is "NAME", then the word */ /* refers to a subject so copy the word to the */ /* subjects array. */ /*****************************************************/ void check_subject() { int i; for (i=0; i= 0; i--) { if ( (strcmp(subjects[i], subjects[sentence]) == 0) && (strcmp(actions[i], actions[sentence]) == 0) && (strlen(places[i]) != 0) ) { make_answer(i); return; } } /* Not enough information in actions and */ /* subjects arrays. */ strcpy(response, "I don't know"); return; } /*****************************************************/ /* Generate a response that states the location of */ /* where the subject and action occured. */ /*****************************************************/ void make_answer(int prev_sentence) { strcpy(response, subjects[prev_sentence]); strcat(response, " "); strcat(response, "was "); get_verb_ing(); strcat(response, places[prev_sentence]); return; } /*****************************************************/ /* Retrieve the ING version of the word from the */ /* dictionary (the ING version of run is running). */ /*****************************************************/ void get_verb_ing() { rewind (infile); fgets(dic_record, 80, infile); while (! feof(infile)) { if (match_verb_ing() == 0) break; fgets(dic_record, 80, infile); } return; } /*****************************************************/ /* If the root in the current dictionary record */ /* matches the root in the actions array, and the */ /* current dictionary record has an ING restriction, */ /* then extract the dictionary word and return 0. */ /*****************************************************/ int match_verb_ing() { int i; char *root; char *dic_word; root = extract_root(); if (strcmp(actions[sentence],root) == 0) { /* Root found, look for ING restriction */ for (i=24; i<33; i++) { if (isspace(dic_record[i])) break; if (dic_record[i] == ING) { dic_word = extract_word(); strcat(response, dic_word); return(0); } } } return(1); } Dictionary file : diction a DET the DET house NOUN street NOUN store NOUN jump NOUN go VERB go goes VERB go going VERB I go went VERB go run VERB run runs VERB run running VERB I run ran VERB run walk VERB walk walks VERB walk walking VERB I walk walked VERB walk jump VERB jump jumps VERB jump jumping VERB I jump jumped VERB jump is AUX was AUX to PREP in PREP on PREP where WH ```