WebWriter++ tries to meet a number of objectives in allowing authors to present code to students—
It might seem that syntax-staining (1) and code mark-up (4) would be incompatible with the other two objectives. However, (1) is easily achieved by importing the source code and parsing and staining it on the fly. This feature was included in the toolset, which leaves only the fourth objective.
Enter pedagogical mark-up which is a small language that allows certain WebWriter++ calls to be embedded in C/C++ comments.
Currently, the following tools are supported:
Indeed, pop-ups and rollover blocks were initially developed specifically to enable the marking of code.
All mark-ups must appear inside C-style (multi-line) comments ('/* */'). Currently WebWriter++ is unforgiving about white space so that pedagogical markups must occur immediately after (and before) the comment operators, as follows:
/*#p="integerPopup"*/int/*#/p*/ count = 0;
Here, the keyword int
has been marked as a pop link. The #p="integerPopup"
command (which is wrapped in comment characters with no intervening spaces) declares that a pop link (which will call up a pop-up named integerPopup) starts at the keyword. Similarly, #/p
marks the end of the link. Thus, the int
keyword will appear as a link to the integerPopup when the code is displayed by WebWriter++.
/*#P="popupName" "The text for the popUp"*/
Creates the pop-up. While html is allowed it does not currently work. A pop-up only has to be defined once (on a particular page). It can be linked multiple times. If you want cross-site pop-ups, use definitions and put them in the dictionary.
/*#p="popupName"*/
Marks the beginning of a pop-up link. The "popupName" should be the name of the pop-up you want to see appear when the link is rolled over.
/*#/p*/
Terminate a pop-up link.
/*#B="blockName"*/
Starts the block. The background colour of the block will change when its link is rolled over. Blocks may be wholly nested within each other.
/*#/B*/
Terminate a block.
/*#b="blockName"*/
Marks the beginning of a rollover block link. The "blockName" should be the name of the rollover block you want to see illuminated when the link is rolled over.
/*#/b*/
Terminate a rollover block link.
/*#d="definitionName"*/
Marks the beginning of a definition link. The "definitionName" should be the name of the definition you want to see pop-up when the link is rolled over. Definitions are kept in a separate, standard dictionary.
/*#/d*/
Terminate a definition link.
Any section of code can be tagged by adding before it
/*#T tagName */
and
/*#/T tagName */
after it. tagName can be any (nonempty) sequent of ascii letters and digits, except for "true" and "false". Leading and trailing whitespace are optional (that is, they are ignored).
A piece of code can carry as many tags as the instructor wishes. Tags do not have to nest. Consider
/*#TA*/Bob /*#/TA*/saw /*#TA*/a /*#TB*/big /*#/TA*/hairy /*#/TB*/monster
Each section is associated with a set of tags as follows:
Section | Tag set |
---|---|
Bob | {A} |
saw | { } |
a | {A} |
big |
{A,B} |
hairy | {B} |
monster | { } |
Tags are used in conjunction with a pair of selection expressions in the insertCode command to control what code sections are displayed by WebWriter++ and in the TM. Given a selection expression E and a section S with tag set T: S is selected if (and only if) E evaluates to true if we replace (in E) each tag in T with "true" and each tag not in T with "false". Only selected sections will be displayed.
Selection expression | Displayed |
---|---|
default (!S&!L) | Bob saw a big |
A | Bob a big |
B | big hairy |
A|B | Bob a big |
A&B | big |
!A | saw hairy monster |
!B | Bob saw a monster |
!A&!B | saw monster |
Here is a larger example.
class MyString{ public: MyString(char* p); // Construct using a standard string MyString(); // "default" (no arguement) constructor MyString(MyString& orig); // Copy constructor ~MyString(); // standard destructor to deal with heap // Accessor functions - used to read object data without changing it int length() ; char getChar(int i) ; // get char at location i void get(char* buff) ; // Get the string & put it into user buff bool compare(MyString& other); // true if equal // Mutator functions - used to change string objects void setChar(int i, char c); // Change char at i to c void changeTo(char* newString); // Change the whole string /* Notice the change to pass by ref to improve efficiency */ void changeTo(MyString& newString); // Function overload private: char* mPtr; // pointer into the heap where the actual string will be int mLength; // length of the string }; /*#TA*/// A class of users for network management class User{ public: User(char* n, char* u, char* p); // Accessors MyString& name(); // return reference int allocation(); // Disk space allowed MyString password(); // return value MyString uname() ; bool confirm(MyString& uName, MyString& pass); // Mutators bool setPassword(MyString& p1, MyString& p2); void setAllocation(int a); protected: MyString mName; // Notice attributes that are OBJECTS! MyString mUname; MyString mPassword; int mAllocation; // Memory allocation, in MBytes };/*#/TA*/ /*#TB*/class Student: public User{ public: Student(char* n, char* id, char* u, char* p); MyString id(); void buy(long amount); bool approvePrint(int pages); protected: MyString mId; long mPrintPennies; };/*#/TB*/ /*#TC*/class Faculty: public User{ public: Faculty(char* n, char* u, char* p, char* today); // accessor functions long pages(); MyString cleared(); // mutator functions bool approvePrint(int pages); // increases page count void clear(char* d); // clears count & notes date protected: long mPages; // Pages used since MyString mCleared; // last date cleared };/*#/TC*/
To expose only class User
on the webSite the code is inserted
using the insertCode
command as follows:
function insertCode("userInherit.cpp", true, "code", "default.cfg", "A&~S&~L", "~S&~L")
which shows only code tagged with "A
" on the eBook page, but the whole example
when it is run in the TM. The ~S
means don't display Scripts, referring
to internal scripting calls which instructors can build into the code, and "~L" means don't show library code. It's a good habit to ensure "~S&~L" is implied by any selections, even when, as in this example, "S" and "L" tags aren't used. The final parameter in this call could be omitted, as "~S&~L" is the default.
Tags "S
" and "Script
" are equivalent and reserved for marking intenal scripting
calls (only "S
" should be used in the insertCode
selection strings).
Tags "L
" and "Lib
" are equivalent and reserved for
marking TM library code(only "L
" should be used in the insertCode
selection strings).
Code that needs to be hidden from regular compilers, but that should be compiled by the the Teaching Machine, can be put in a comment
/*#I code goes here*/
WebWriter will not show these comments at all (not even if the selection string is "S"). The Teaching Machine will treat the code as if it were tagged with "S" (or, equivalently, "Script"). See below for examples.
The TM will not display code that is currently unselected. Completely unselected lines are indicated by an ellipsis (...) when line numbers are displayed. For this reason, we suggest using a configuration file that turns off line numbers when scripting code is used.
You can use the TM's View menu to change the current selection.
Even white space that is selected will mean that the line is partially selected, i.e., not completely unselected.
Furthermore the TM will generally not stop on code that is on a completely unselected line. So scripting calls should be on completely unselected lines. The same mechanism is used to keep the TM from stopping in library code. (Library code is tagged with "L", and so is not selected by default.
The best way to mark up TM code is like this
#include <iostream> #include <stdlib.h>/*#I #include <ScriptManager> */ . . . int array[SIZE]; /*#I // Use only single line comments in invisible regions. ScriptManager::relay("ArrayBar", "setArray", array);*/ for (int i = 0; i < SIZE; i++) { array[i] = 0 ; }
or like this
#include <iostream> #include <stdlib.h>/*#T S*/ #include <ScriptManager> /*#/T S*/ . . . int array[SIZE]; /*#T S*/ /* Either kind of comment is allowed here.*/ ScriptManager::relay("ArrayBar", "setArray", array);/*#/T S*/ for (int i = 0; i < SIZE; i++) { array[i] = 0 ; }
The first style is best if you don't want other compilers to see the code.
Note that there must be nothing --not even be spaces or tab characters-- after the final "*/". Whitespace characters after the final "*/" will be selected, by default, and so make the line appear in the TM. (This may change in the future. It's not a bug, but it is an annoyance.)
If the last line before the script code ends with a single line comment, you can do the following
int array[SIZE]; // Make an array. /*#I ScriptManager::relay("ArrayBar", "setArray", array); */ for (int i = 0; i < SIZE; i++) { array[i] = 0 ; }
Note the funky indentation; it is deliberate. It is tempting here to put the "*/" before the newline. This would be fine for the TM, but WW would show a blank line before the "for" loop.
While pedagogically marked code compiles just fine it is still messy and confusing for students to look at. Consequently, both WebWriter++ and the Teaching Machine automatically remove pedagogical markups from their displays.