slideList is[s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16,s17,s18,s19,s20,s21,s22]; s1 is Slide("

Exploring and understanding JS-Eden Parsing model

Guidance for exploring JS-EDEN parsing model using the EM presentation environment

This session is an example of exploring JS-Eden parsing model with the expression grammar.

JS-Eden parsing model is a simulation of traditional parser with EM spirits. It demonstrates the whole process of parsing in detail and parses given strings according to certain grammar.

The model is a parser written in JS-Eden, which allows users to write a model in a new notation without using any external compiler. It also benefits users who are interested in parsing theory, making them understand the process of parsing during their exploring.

"); s2 is Slide(" Setting up the Model and Grammar information

In this session we are using the expression grammar as an example. First let us learn about the expression grammar by executing the code below.

include(\"models/parsingEmpe/funcGrammar.js-e\");

The first four lines shows the accept string and the reduction rules.
The remaining part is the finate state machine generated by the Yacc.
expr- expression symbol:e
ID- identifier symbol:i
func-function symbol:f
LPAREN-left parenthesis symbol:(
RPAREN-right parenthesis symbol:)

The grammar displayed on the canvas is for us to learn about, JS-Eden do not understand this file.

Hence we should transform the information of grammar into some form which JS-Eden can interpret.

"); s3 is Slide("

Setting up the Model and Grammar information

In order to achieve this, we can use a list with 12 elements because the finite state machine has 12 states

For example, from the grammar, we see that in state 1, reading a LPAREN, we should go
to state 4. This piece of information will be stored in the list as a 3-tuple element of the first entry,[1,\"LPAREN\",4];

Even though we can use a list to store grammar information, transforming all the information manually is still a tiresome work. To avoid this, we could use a file mkeden(to be run in sed environment):

mkeden file

The mkeden file will transform the grammar information in to a list with 12 states. The list should be: include(\"models/parsingEmpe/funcTransitionsPicture.js-e\"); "); s4 is Slide("

Setting up the parsing model environment

To set up the parsing model, execute the code below:

include(\"models/parsingEmpe/parsingModelRun.js-e\");

Now we have a plain canvas. In order to understand how traditional parser works, the next thing to do is to make a visualisation of the finite state machine on the canvas.

We are going to use the graph tool in JS-Eden. Before starting adding something to the canvas, we need to set values for two observables: numberOfNodes(number of states) and numberOfSyms(number of symbols appeared in the grammar).

numberOfNodes=12; numberOfSyms=7;

and also the graphscale, which determines the size of the graph.

graphscale=0.5; "); s5 is Slide("

Visualisation of the finite state machine:Nodes addNode(mygraph,\"n0\");
The red node on the canvas is a graph representation of node 0(state 0). Of course, we want to have more:
addNode(mygraph,\"n1\"); addNode(mygraph,\"n2\"); addNode(mygraph,\"n3\"); We will add a label to each node to distinguish the nodes. makeNodeLabel(0); makeNodeLabel(1); makeNodeLabel(2); makeNodeLabel(3); Following this procedure, we can draw all the 12 nodes on the canvas: addNode(mygraph,\"n4\");makeNodeLabel(4); addNode(mygraph,\"n5\");makeNodeLabel(5); addNode(mygraph,\"n6\");makeNodeLabel(6); addNode(mygraph,\"n7\");makeNodeLabel(7); addNode(mygraph,\"n8\");makeNodeLabel(8); addNode(mygraph,\"n9\");makeNodeLabel(9); addNode(mygraph,\"n10\");makeNodeLabel(10); addNode(mygraph,\"n11\");makeNodeLabel(11);

"); s6 is Slide("

Visualisation of the finite state machine: Edges

Edges mean transitions between states and can be added to the canvas. addEdge(mygraph,\"n1\",\"n4\");
We can see an arrowed line pointing from source(n1) to target(n4), which indicates there is an available transition from state 1 to state 4.
A label for an edge means the symbol to be read in order to carry out this transition. From the grammar, we know that the symbol for transition from state 1 to state 4 is LPAREN(\"(\"). makeEdgeLabel(1,4); Consequently, according to the actual transitions from the grammar, the rest of the edges can be shown on the canvas: addEdge(mygraph,\"n3\",\"n5\"); makeEdgeLabel(3,5); addEdge(mygraph,\"n4\",\"n1\"); makeEdgeLabel(4,1); addEdge(mygraph,\"n4\",\"n2\"); makeEdgeLabel(4,2); addEdge(mygraph,\"n4\",\"n6\"); makeEdgeLabel(4,6); addEdge(mygraph,\"n6\",\"n7\"); makeEdgeLabel(6,7); addEdge(mygraph,\"n7\",\"n1\"); makeEdgeLabel(7,1); addEdge(mygraph,\"n7\",\"n2\"); makeEdgeLabel(7,2); addEdge(mygraph,\"n7\",\"n8\"); makeEdgeLabel(7,8); addEdge(mygraph,\"n8\",\"n9\"); makeEdgeLabel(8,9); addEdge(mygraph,\"n9\",\"n1\"); makeEdgeLabel(9,1); addEdge(mygraph,\"n9\",\"n2\"); makeEdgeLabel(9,2); addEdge(mygraph,\"n9\",\"n10\"); makeEdgeLabel(9,10); addEdge(mygraph,\"n10\",\"n11\"); makeEdgeLabel(10,11); addEdge(mygraph,\"n0\",\"n1\"); makeEdgeLabel(0,1); addEdge(mygraph,\"n0\",\"n2\"); makeEdgeLabel(0,2); addEdge(mygraph,\"n0\",\"n3\"); makeEdgeLabel(0,3);

"); s7 is Slide("

Change the Graph

In addition to displaying nodes and edges on the canvas, you can also moving them by clicking. A procedure is required to move each node on the canvas: proc mouseMove_s1: mousePressed { if(((mouseX-ps1x)*(mouseX-ps1x)<100 )&&((mouseY-ps1y)*(mouseY-ps1y)<100)) { if(mousePressed) { ps1x is mouseX; ps1y is mouseY; } else{ ps1x = mouseX; ps1y = mouseY; } } }
Now s1 is movable. To move other nodes is routine. Just put the node's name which you like to move in place of s1 in the above code.
However the whole process does not seem fast, you can try to use the tool definition factory(also available in JS-Eden) to save time. In this session, we won't go further with the use of definition factory, you can explore it yourself. The code to make all the nodes movable: include(\"models/parsingEmpe/moveNodes.js-e\");

"); s8 is Slide("

In previous slides, we have stored the grammar information in a JS-Eden list. In addition, we also want it to appear on one window, where we can refer to when trying to parse a string manually.

parsingrules is a list which stores the grammar information. It is different from transitions, in that each of its entry is one line in the grammar, in the human-readable form and it does not store any information about the finite state machine. parsingrules is [line1,line2,line3]; line1=\"$accept: expr $end\"; line2=\"1 expr: ID\"; line3=\"2 | func3 LPAREN expr COMMA expr COMMA expr RPAREN\";
In the window named \"CANVAS HTML5 view_3\", you will see the grammar information displayed.


The graph adjacent to the slides is the finite state machine, it shows the state of parser at each stage. To distinguish between edges traversed and edges not traversed, we are going to make the traversed edges to be black, and others to remain in their default color.

In order to do this, we need a function which returns how many times each edge has been traversed, and observables responsible for the colours of the edges, then make a dependency on them.

"); s9 is Slide("

Dependency between the color of the edge and number of times it has been traversed.

Suppose we have an observable cols0s1, which is the color of the edge s0s1, What kind of dependency do we want?
We want the color of the edge dependent on the times it has been traversed. Obviously, we need another observable of edge s0s1 for the number of times it has been traversed.

ctxy is a function, which takes three parameters: the start point,the end point of an edge and currpath (observable for current path traversed), and returns the number of times that this edge has been traversed.
So if we make the colour of the edge depend on the number of times it has been traversed, the colours of the edges together will clearly show the path. For edge s0s1,

cts0s1 is ctxy(0,1,currpath); cols0s1 is cts0s1>0 ? \"black\": defaultcolour(0,1,transitions[12]); "); s10 is Slide("

To define the colours of all the edges:

cts0s1 is ctxy(0,1,currpath);cts0s3 is ctxy(0,3,currpath); cts0s2 is ctxy(0,2,currpath);cts10s11 is ctxy(10,11,currpath); cts9s2 is ctxy(9,2,currpath);cts9s1 is ctxy(9,1,currpath); cts9s10 is ctxy(9,10,currpath);cts8s9 is ctxy(8,9,currpath); cts4s1 is ctxy(4,1,currpath);cts4s2 is ctxy(4,2,currpath); cts4s6 is ctxy(4,6,currpath);cts3s5 is ctxy(3,5,currpath); cts7s1 is ctxy(7,1,currpath);cts7s2 is ctxy(7,2,currpath); cts7s8 is ctxy(7,8,currpath);cts6s7 is ctxy(6,7,currpath); "); s11 is Slide(" cols0s1 is cts0s1>0 ? \"black\": defaultcolour(0,1,transitions[12]); cols0s2 is cts0s2>0 ? \"black\": defaultcolour(0,2,transitions[12]); cols0s3 is cts0s3>0 ? \"black\": defaultcolour(0,3,transitions[12]); cols10s11 is cts10s11>0 ? \"black\": defaultcolour(10,11,transitions[10]); cols9s10 is cts9s10>0 ? \"black\": defaultcolour(9,10,transitions[9]); cols9s2 is cts9s2>0 ? \"black\": defaultcolour(9,2,transitions[9]); cols9s1 is cts9s1>0 ? \"black\": defaultcolour(9,1,transitions[9]); cols8s9 is cts8s9>0 ? \"black\": defaultcolour(8,9,transitions[8]); cols4s1 is cts4s1>0 ? \"black\": defaultcolour(4,1,transitions[4]); cols4s2 is cts4s2>0 ? \"black\": defaultcolour(4,2,transitions[4]); cols4s6 is cts4s6>0 ? \"black\": defaultcolour(4,6,transitions[4]); cols3s5 is cts3s5>0 ? \"black\": defaultcolour(3,5,transitions[3]); cols7s8 is cts7s8>0 ? \"black\": defaultcolour(7,8,transitions[7]); cols7s2 is cts7s2>0 ? \"black\": defaultcolour(7,2,transitions[7]); cols7s1 is cts7s1>0 ? \"black\": defaultcolour(7,1,transitions[7]); cols6s7 is cts6s7>0 ? \"black\": defaultcolour(6,7,transitions[6]); "); s12 is Slide("

Key Observables:

currpath - stores the content on the state stack as a list.
currstack - current content on the stack as a list.
currrule - the number of the grammar rule the parser determines that all the items on the right-hand side of which have been seen(the production by which the parser reduces).
currrstate - current state of the parser (the last element of the currpath, or the top element on the stack state).

nextsymbol - represents the look ahead token.

isreduction - reads the transformations table and decides if the parser should reduce in the current state. If there is a shift-reduce conflict, it always chooses to shift.

InputText - the input string, stays the same all the time.
InputTextt - same as the input string at the beginning, but changes according to the reduction.

k - the index of the current symbol in the InputText (how far we have read in the InputText).
j - the index of the current symbol in the InputTextt (how far we have read in the InputTextt).
The observables k and j are used to control how many tokens the parser has read from the input. The values of k and j are increased by 1 every time the \"next step\" button is clicked. "); s13 is Slide("

Making dependencies

"); s14 is Slide("

Lexical Analysis

The lexical analysis part of parsing models in JS-EDEN is different from the lexical analysis of the traditional parser. This is done by using the function subpatt, which is an example of embedding JavaScript in EDEN: func substpatt { ${{ var th = arguments[0]; var repatt = arguments[1]; var reattr = arguments[2]; var repstr = arguments[3]; return th.replace(RegExp(repatt, reattr), repstr); }}$; } This function recognizes regular expressions and the set of tokens are defined by regular expressions.

If an input string is given, the model should find IDs and exprs, and mark them as identifiers.

proc makeinputtext : inputstr{ InputText=inputstr; InputText = substpatt(InputText,\"[A-Za-z][a-zA-Z0-9]*\",\"g\",\"x\"); InputText = substpatt(InputText,\"[0-9]+\",\"g\",\"v\"); InputTextEnd = oneline(InputText)//\"$\"; InputTextt=InputText//\"$\"; } "); s15 is Slide("

Input values

Using the subspatt function, we substitute values in the input by lexified tokens. And we want to keep a record of the original input values, and store them into a list.

The record of the input values, can be used in the last step of parsing - syntax directed translation. So we make a function named makeinputvals. This function will be different for different grammars. It makes uses of the function findpatt(which finds and returns a regular string):

func findpatt { ${{ var th = arguments[0]; var repatt = arguments[1]; var reattr = arguments[2]; var result = th.match(RegExp(repatt, reattr)); if(result==null) return \"\"; else return result; }}$ }

and analyzes the input to many cases. Below is an example of the makeinputvals function for eddi grammar. Can you make a makeinputvals function for the func3 grammar? After please include the function in the input window.

func makeinputvals { para input, val; auto i,j,result,found,vals; vals=val.slice(0); j=1;result=[]; for(i=1;i<=input#;i++) { if(input[i]==\"x\") { for(j=1;j<=vals#;j++) { if (findpatt(vals[j],\"[A-Za-z][a-zA-Z0-9]*\",\"g\") != \"\") { result=result//vals[j]; vals[j]=\"\"; break; } else writeln(\"syntax error\"); } }else if (input[i]==\"v\") { for(j=1;j<=vals#;j++) { if (findpatt(vals[j],\"[0-9]+\",\"g\") != \"\") { result=result//vals[j]; vals[j]=\"\"; break; } } }else {result=result//[\"\"];} } return result; }

"); s16 is Slide("

Shift and Reduce

When the value of k increases 1, the parsing model reads the next token and shifts. In some certain points of the parsing, the parsing model should reduce according to the grammar rules. This means that it should change some thing on the top of the stack. So to do reduction, we make functions which takes InputTextt as parameter, and returns the changed value of InputTextt.

In func3 grammar, we have 2 reductions rules:

1 expr: ID
2 | func3 LPAREN expr COMMA expr COMMA expr RPAREN

The first rule says that if an id on the stack, replace it by expr. The second says that if func3 LPAREN expr COMMA expr COMMA expr RPAREN appears on the stack, replace it by expr. The thing to keep in mind is that after changing the value on the stack, we also need to update the values of j.

func substituteI{ para cstack; auto result; if(currstack#-1>0) result=substr(cstack,1,currstack#-1)//\"e\"//nextpart; else result=\"e\"//nextpart; return result; } func substituteFLR{ para cstack; auto size, result; size=currstack#-8; if(size>0) result=substr(cstack,1,size)//\"e\"//nextpart; else result=\"e\"//nextpart; return result; }

Then we need a function to decide which of the substitute function should be called: proc applyrule{ para ruleno; if (ruleno==1) execute(\"InputTextt=substituteI(InputTextt);\"); else if (ruleno==2) execute(\"InputTextt=substituteFLR(InputTextt);\"); } "); s17 is Slide("

Syntax Directed Translation

In parallel with the observables InputTextt and currstack, we have observables inputvals and stackvals to record the values of the input. When the parsing model reduces by rule 1, the inputvals is not changed. But when it reduces by rule 2, the inputvals changes as it combines the eight elements to one entry.

func substituteFLR2{ para vals; auto size,calc, result; size=stackvals#-8; calc=j; if(size>0) result=sublist(vals,1,size)//[vals[calc-1]]//nextvals; else result=[vals[calc-1]]//nextvals; return result; } proc evalrule{ para ruleno; if (ruleno==1) execute(\"inputtvals=inputtvals;\"); else if (ruleno==2) execute(\"inputtvals=substituteFLR2(inputtvals);\"); } "); s18 is Slide("

Assertions relevant to understanding the winning strategy

Exercise 5: Mathematical proofs are typically constructed through making conjectures and looking for evidence to confirm or refute them. Here are some assertions that came to mind in formulating a proof of the existence of a Nim winning strategy. See if you can find out whether they are valid, open questions or misconceptions.

"); s19 is Slide("

To access answers to the questions on the previous slide, you can execute the following redefinition: slidelist=[slide10,slide11,slide12,slide13,slide14, slide15,slide16,slide17,slide18,slide19, slide20,slide21,slide22,slide23,slide25, slide26,slide27,slide28,slide32,slide29, slide30,slide31];

To hide answers to the questions on the previous slide, you can execute the following redefinition: pres_slides=[slide10,slide11,slide12,slide13,slide14, slide15,slide16,slide17,slide18,slide19, slide20,slide21,slide22,slide23,slide25, slide26,slide27,slide28,slide32, slide30,slide31];

If appropriate, answers will be shown on the next slide.

"); s20 is Slide("

Answers

The assertions are respectively false, false and true. Consider, for instance: pileSizeOne = 11; pileSizeTwo =14; pileSizeThree = 6;

These observations respectively disprove assertions 1 and 2. Taken together, they prove the validity of assertion 3.

"); s21 is Slide("

Exercise 6:

  1. Add a textual annotation of the form \"This situation is won/lost with best play\", linked by dependency to the current Nim-Sum.
  2. Change the background colour of clickable buttons on the interface to distinguish them from mere display panels.
  3. Adapt the interface so that when play is in progress, the text showing the active pile is highlighted in green and the text for the other two piles is red.
  4. Adapt the interface to the windows displaying piles of stones so that you can click on individual stones to remove them.
  5. Dispose the stones in a 8-4-2-1 rather than a 5-4-3-2-1 formation in the winPileOne, winPileTwo and winPileThree windows, and display the stones in a way that reflects the binary representation of the pile size (so that for instance, when there are five stones in a pile, this is displayed as 4+1 stones).
  6. When the Nim-Sum is non-zero highlight the piles from which stones can be taken to achieve Nim-Sum zero, and display the number of stones to be taken.
  7. Automate a Nim player that implements best play.



"); s22 is Slide("

Empirical Modelling as disposition

Disposing the stones in 8-4-2-1 formation is interesting in cognitive terms. It makes it much easier to execute the winning strategy when playing Nim.

To test this out, you can load the file dispose_8_4_2_1.e which provides a solution to Exercise 6.5. include(\"dispose_8_4_2_1.e\");

");