@@ -294,7 +294,16 @@ def is_done(self):
294294 return self .top () == self .head () == HASH
295295
296296 def match (self ):
297- """Attempts a match move and returns the corresponding new instantaneous description."""
297+ """Performs a match move.
298+
299+ If the top of the stack is ε, it will be removed and the tape head will be left unchanged.
300+
301+ Raises:
302+ ValueError: If the top of the stack and tape head symbol are not equal.
303+ Returns:
304+ A new updated instantaneous description.
305+ """
306+ # the stack and tape can never be empty
298307 if (self .top () == ε ) or (self .top () in self .G .T and self .top () == self .head ()):
299308 c = copy (self )
300309 c .stack = copy (c .stack )
@@ -305,14 +314,24 @@ def match(self):
305314 raise ValueError ('The top of the stack and tape head symbol are not equal.' )
306315
307316 def predict (self , P ):
308- """Attempts a prediction move, given the specified production, and returns the corresponding new instantaneous description."""
317+ """Performs a prediction move, given the specified production.
318+
319+ If the production is an epsilon production, the stack will be left unchanged.
320+
321+ Args:
322+ P (:class:`~liblet.grammar.Production`): The production to predict.
323+ Raises:
324+ ValueError: If the production does not belong to the grammar, or if the lhs does not correspond to the top of the stack.
325+ Returns:
326+ A new updated instantaneous description.
327+ """
309328 if P in self .G .P and self .top () == P .lhs :
310329 c = copy (self )
311330 c .stack = copy (c .stack )
312331 c .stack .pop ()
313332 c .steps += (P ,)
314- for X in reversed ( P . rhs ):
315- if ε != X :
333+ if not P . is_epsilon ( ):
334+ for X in reversed ( P . rhs ) :
316335 c .stack .push (X )
317336 return c
318337 raise ValueError ('The top of the stack does not correspond to the production lhs.' )
@@ -337,17 +356,45 @@ def is_done(self):
337356 """Returns `True` if the computation is done, that is if the stack contains the a tree rooted in G.S and the head is at the tape end."""
338357 return len (self .stack ) == 1 and len (self .tape ) == self .head_pos and self .top () == self .G .S
339358
340- def shift (self ):
341- """Performs a shift move and returns the corresponding new instantaneous description."""
359+ def shift (self , consume = True ):
360+ """Performs a shift move.
361+
362+ If the top of the stack is ε, it will be removed before shifting.
363+
364+ Args:
365+ consume (bool): If `True`, the head position is increased so that the symbol under the tape head is consumed.
366+ Otherwise, the head does not move and an ε is pushed. The default is `True`.
367+
368+ Returns:
369+ A new updated instantaneous description.
370+ """
342371 c = copy (self )
343- c .stack .push (Tree (c .head ()))
344- c .head_pos += 1
372+ if self .stack and self .top () == ε :
373+ c .stack .pop ()
374+ if consume :
375+ if self .head_pos < len (self .tape ):
376+ c .stack .push (Tree (c .head ()))
377+ c .head_pos += 1
378+ else :
379+ raise ValueError ('The head is already at the end of the tape.' )
380+ else :
381+ c .stack .push (Tree (ε ))
345382 return c
346383
347384 def reduce (self , P ):
348- """Attempts a reduce move, given the specified production, and returns the corresponding new instantaneous description."""
385+ """Performs a reduce move, given the specified production.
386+
387+ Args:
388+ P (:class:`~liblet.grammar.Production`): The production to reduce.
389+ Raises:
390+ ValueError: If the production does not belong to the grammar, if the rhs does not correspond to the symbols on the stack or the stack is empty.
391+ Returns:
392+ A new updated instantaneous description.
393+ """
349394 if P not in self .G .P :
350395 raise ValueError ('The production does not belong to the grammar.' )
396+ if not self .stack :
397+ raise ValueError ('The stack is empty.' )
351398 c = copy (self )
352399 children = [c .stack .pop () for _ in P .rhs ][::- 1 ]
353400 if tuple (t .root for t in children ) != P .rhs :
0 commit comments