/* class comment */!!

inherit(YaccMachine, #CLIParser, #(serverHandle  /* cli server who's driving us */
parseTreeNestingLevel
  /* if 0, execute parse tree; otherwise, add on      to it */), 2, nil)!!

setClassVars(CLIParser, #())!!

now(class(CLIParser))!!

/* create, initialize parser. */
Def open(self | temp)
{
  temp := new(CLIParser);
  initialize(temp);
  ^temp;
} 
!!

now(CLIParser)!!

Def doOverwrite(self)
{
  yyVal := init(new(CLIOverwriteNode));
}!!

Def doAppend(self)
{
  yyVal := init(new(CLIAppendNode));
}!!

Def doStopTimer(self)
{
  yyVal := init(new(CLIStopTimerNode));
}!!

Def doStartTimer(self)
{
  yyVal := init(new(CLIStartTimerNode));
}!!

Def doLapTime(self)
{
  yyVal := init(new(CLILapTimerNode));
}!!

/* remove nesting level */
Def decrementNestLevel(self | errorMessage)
{
  parseTreeNestingLevel := parseTreeNestingLevel - 1;
  if parseTreeNestingLevel < 0
  then
    errorMessage := getFormattedError(ErrorTextLibClass$Inst, 
         ER_CLI_INTERNAL_ERROR, "CLIParser:decrementNestLevel", nil, nil);
    syntaxError(self, nil, errorMessage);
    /* syntaxError unravels call stack, and does not return */
  endif;
}
!!

Def doAlias(self)
{
 /* Alias List node: (aliasname) */
 yyVal := init(new(CLIListAliasNode),
  nil);  /* list all */
}!!

Def doAliasAssign(self)
{
 /* Alias Assign node: (alias, value) */
 yyVal := init(new(CLISetAliasNode),
  item(self,1), item(self,2));
}!!

Def doAliasDelete(self)
{
 /* AliasDelete node: (alias) */
 yyVal := init(new(CLIDelAliasNode),
  item(self,2));
}!!

Def doAliasName(self)
{
 /* Alias List node: (aliasname) */
 yyVal := init(new(CLIListAliasNode),
  item(self,1));
}!!

Def doAssignment(self)
{
 /* Assignment node: (target, value, source) */
 yyVal := init(new(CLIAssignmentNode),
  item(self,0), item(self,2),
  nil);
}!!

Def doBinaryOp(self)
{
 /* Binary Op node: (left, right, operation) */
 yyVal := init(new(CLIBinaryOpNode),
  item(self,0), item(self,2), item(self,1));
}!!

Def doBlock(self)
{
  /* no tree node necessary; just return insides of block expr */
  yyVal := item(self,1);
  decrementNestLevel(self);
}!!

Def doCall(self)
{
 /* Call node: (callee, arglist) */
 yyVal := init(new(CLICallNode),
  item(self,1), item(self,2));
}!!

Def doCallListKeyword(self)
{
 /* call list keyword node: (string) */
 yyVal := init(new(CLIListKeywordNode),
  item(self,0));
}!!

Def doCallListOperator(self)
{
 /* call list operator node: (operator) */
 yyVal := init(new(CLICallOperatorNode),
  item(self,0));
}!!

Def doCallListQString(self)
{
 /* call list quoted string node: (string) */
 yyVal := init(new(CLIListStringNode),
  unquote(CLIUtilities, item(self,0)));
}!!

Def doCallListString(self)
{
 /* call list string node: (string) */
 yyVal := init(new(CLIListStringNode),
  item(self,0));
}!!

Def doCallListVariable(self)
{
 /* call list variable node: (string) */
 yyVal := init(new(CLIListVariableNode),
  item(self,0));
}!!

Def doCallParam(self)
{
 /* Param node: (list, param) */
 yyVal := init(new(CLICallParmList),
   item(self,0));
}!!

Def doCallParamList(self | t1, t0)
{
  yyVal := add(item(self, 0), item(self, 1));
}!!

Def doClear(self)
{
  yyVal := init(new(CLIClearNode));
}!!

Def doDeleteVar(self)
{
  /* delete variable node */
  yyVal := init(new(CLIDelVarNode),
    item(self,1));
}!!

Def doExit(self)
{
  yyVal := init(new(CLIExitNode));
}!!

Def doExpr(self)
{
  yyVal := init(new(CLIExprNode),
    item(self,0));
}!!

Def doHelp(self)
{
  yyVal := init(new(CLIHelpNode),
    nil,  /* no help topic */
    nil); /* not an internal help */
}!!

Def doHelp_(self)
{
  yyVal := init(new(CLIHelpNode),
    nil,  /* no help topic */
    0);   /* is an internal help */
}!!

Def doHelp_Ident(self)
{
  yyVal := init(new(CLIHelpNode),
    quote(CLIUtilities, item(self,1)), /* quote the identifier */
    0);   /* is an internal help */
}!!

Def doHelp_Primitive(self | temp)
{
  temp := tokenValueToKeyword(CLIUtilities, item(self, 1));
  yyVal := init(new(CLIHelpNode),
    quote(CLIUtilities, temp),
    0);  /* internal help */
}!!

Def doHelp_Topic(self)
{
  yyVal := init(new(CLIHelpNode),
    item(self,1),  /* help topic */
    0);    /* is an internal help */
}!!

Def doHelpIdent(self)
{
  yyVal := init(new(CLIHelpNode),
    quote(CLIUtilities, item(self,1)), /* quote the identifier */
    nil); /* not an internal help */
}!!

Def doHelpPrimitive(self | temp)
{
  temp := tokenValueToKeyword(CLIUtilities, item(self, 1));
  yyVal := init(new(CLIHelpNode),
    quote(CLIUtilities, temp),
    nil);  /* not an internal help */
}!!

Def doHelpTopic(self)
{
  yyVal := init(new(CLIHelpNode),
    item(self,1),  /* help topic */
    nil);  /* not an internal help */
}!!

Def doHistory(self)
{
  yyVal := init(new(CLIHistoryNode),
    nil);  /* nil size indicates "show" */
}!!

Def doHistorySize(self)
{
  yyVal := init(new(CLIHistoryNode),
    item(self,1));
}!!

Def doIdentifier(self)
{
 yyVal := init(new(CLIIdentNode),
  item(self,0));
}!!

Def doIf(self)
{
 /* If node: (expression, body) */
 yyVal := init(new(CLIIfNode),
  item(self,2), item(self,4));
}!!

Def doIfElse(self)
{
 /* IfElse node: (expression, if_body, else_body) */
 yyVal := init(new(CLIIfElseNode),
  item(self,2), item(self,4), item(self,6));
}!!

Def doInteger(self)
{
 /* type test node: (value) */
 yyVal := init(new(CLITypeIntegerNode),
   item(self,2));
}!!

Def doList(self)
{
  yyVal := init(new(CLIVarListNode),
    nil);  /* list all vars */
}!!

Def doListVar(self)
{
  yyVal := init(new(CLIVarListNode),
    item(self,1));  /* variable name */
}!!

Def doLog(self)
{
  yyVal := init(new(CLILogFileNode),
    nil);  /* no log file name indicates "show" */
}!!

Def doLogFileName(self)
{
  yyVal := init(new(CLILogFileNode),
    item(self,1));
}!!

Def doLogging(self)
{
  yyVal := init(new(CLIShowNode),
    TLOGGING);
}!!

Def doLoggingOff(self)
{
  yyVal := init(new(CLISetNode),
    TLOGGING, TOFF);
}!!

Def doLoggingOn(self)
{
  yyVal := init(new(CLISetNode),
    TLOGGING, TON);
}!!

Def doNoCallParams(self)
{
 yyVal := initEmpty(new(CLICallParmList));
}!!

Def doParen(self)
{
  /* parenthesis node: (value) */
  /* preserve parenthesis in source */
  yyVal := init(new(CLIParenNode),
    item(self,1));
}!!

/* this is just another (friendlier?) way to get to the expression node */
Def doPrintExpr(self)
{
  yyVal := init(new(CLIExprNode),
    item(self,2));
}!!

Def doQuotedString(self)
{
 yyVal := init(new(CLIQuotStringNode),
  item(self,0));
}!!

Def doRestore(self)
{
  yyVal := init(new(CLIRestoreNode),
    item(self,1));
}!!

Def doSave(self)
{
  yyVal := init(new(CLISaveNode),
    item(self,1));
}!!

Def doShowCmd(self)
{
  yyVal := init(new(CLIShowNode),
    TECHO);
}!!

Def doShowCmdOff(self)
{
  yyVal := init(new(CLISetNode),
    TECHO, TOFF);
}!!

Def doShowCmdOn(self)
{
  yyVal := init(new(CLISetNode),
    TECHO, TON);
}!!

Def doShowRes(self)
{
  yyVal := init(new(CLIShowNode),
    TRESULTS);
}!!

Def doShowResOff(self)
{
  yyVal := init(new(CLISetNode),
    TRESULTS, TOFF);
}!!

Def doShowResOn(self)
{
  yyVal := init(new(CLISetNode),
    TRESULTS, TON);
}!!

Def doStatements(self)
{
}!!

Def doStmt(self)
{
  if okToExecuteStatement(self)
  then
    /* generate the actor code */
    CLICompile(init(new(CLIStatement), item(self, 0)));
    /* execute the code that has been generated */
    executeActorCode(self);
    argTupleInitTable(getExecutionEngine(CLIExecEngine));
  else
    yyVal := init(new(CLIStmtList), item(self,0));
  endif;
}!!

Def doStmtEmpty(self)
{
  /* nothing to do if empty top-level statement */
  if not(okToExecuteStatement(self))
  then
    yyVal := init(new(CLIStmtList), nil);
  endif;
}!!

Def doStmtList(self)
{
  if okToExecuteStatement(self)
  then
    /* generate the actor code */
    CLICompile(init(new(CLIStatement), item(self, 2)));
    /* execute the code that has been generated */
    executeActorCode(self);
    argTupleInitTable(getExecutionEngine(CLIExecEngine));
  else
    yyVal := add(item(self,0), item(self,2));
  endif;
}!!

Def doString(self)
{
 /* type test node: (value) */
 yyVal := init(new(CLITypeStringNode),
   item(self,2));
}!!

Def doTimestamp(self)
{
  yyVal := init(new(CLITimestampNode));
}!!

Def doUnaryOp(self)
{
 /* Unary Op node: (value, operation) */
 yyVal := init(new(CLIUnaryOpNode),
  item(self,1), item(self,0));
}!!

Def doUnknown(self)
{
 yyVal := init(new(CLIUnknownNode),
  item(self,0));
}!!

Def doVariable(self)
{
 yyVal := init(new(CLIVariableNode),
  item(self,0));
}!!

Def doWhile(self)
{
 /* While node: (expression, body) */
 yyVal := init(new(CLIWhileNode),
  item(self,2), item(self,4));
}!!

Def doWorkDirSet(self)
{
  yyVal := init(new(CLIWorkDirNode),
    item(self,1));  /* new work dir */
}!!

Def doWorkDirShow(self)
{
  yyVal := init(new(CLIWorkDirNode),
    nil);
}!!

/* execute the actor code that has been generated up to this point */
Def executeActorCode(self | col val /* formatter lines  */ errorMessage)
{
  col := copyFrom(CLIStream, 0, position(CLIStream));
  /* make sure code size is OK */
  if size(col) > 5500  /* empirically determined */
    errorMessage := getFormattedError(ErrorTextLibClass$Inst, 
         ER_CLI_COMMAND_SIZE, asciiz(asStringRadix(size(col), 10)),
         nil, nil);
    syntaxError(self, nil, errorMessage);
  endif;
  
  setCollection(CLIStream, "");
  reset(CLIStream);
  /* start of debug lines - to print generated Actor source to workspace */
  /*
  formatter := analyze(ActorAnalyzer, col);
  lines := formatLines(formatter, 60);
  outputLine(presenter(serverHandle), "****source lines****");
  outputTextCollection(serverHandle, lines);
  */
  /* end of debug lines */
  onError(ErrorHandler:late, self, #all, #all, 
    {using(receiver, bp, errorBp)
     runtimeError(self);
     0;
    });
  val := parse(col, getExecutionEngine(CLIExecEngine));
  offError(ErrorHandler);
}!!

/* add nesting level */
Def incrementNestLevel(self)
{
  parseTreeNestingLevel := parseTreeNestingLevel + 1;
}
!!

/* initialize */
Def initialize(self)
{
  resetNestLevel(self);
}
!!

/* if parseTreeNestingLevel = 0, OK to execute statement; otherwise not. */
Def okToExecuteStatement(self)
{
  ^(parseTreeNestingLevel = 0);
}
!!

/* Parse the string currently held by the lexical analyzer. */
Def parse(self)
{
  setCollection(CLIStream, "");
  reset(CLIStream);
  reset(lex);
  clear(self);
  acc := nil;
  loop
    push(self);
    newState(self);
  while not(acc)
  begin
  endLoop;
  ^collection(CLIStream);
}!!

/* register the server with the parser; parser occasionally needs access. */
Def registerServerHandle(self, s)
{
  serverHandle := s;
}
!!

/* perform reinitialization after command processing resulted in an error. */
Def reInit(self)
{
  resetNestLevel(self);
  /* reset stream; required when rejecting a command that is too long;
     see ppr 5257 */
  setCollection(CLIStream, "");
  reset(CLIStream);
}
!!

/* initialize */
Def resetNestLevel(self)
{
  parseTreeNestingLevel := 0;
}
!!

/* process a runtime error */
Def runtimeError(self)
{
  outputExplicit(presenter(serverHandle), "runtime error: " +
    loadString(exceptionSelector(ErrorHandler)));
  reInit(serverHandle);
  ^0;
}
!!

/* return server. */
Def serverHandle(self)
{
  ^serverHandle;
}!!

/* this is called when the Yacc Machine detects a parsing error */
Def showError(self, loc0 | errorMessage)
{
  errorMessage := getFormattedError(ErrorTextLibClass$Inst, 
     ER_CLI_SYNTAX, nil, nil, nil);
  syntaxError(self, nil, errorMessage);
}!!

Def syntaxError(self, bp, str)
{
  ^syntaxError(serverHandle, bp, str)
}!!

/* CLIParser Class Initialization Code */
