Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1046 lines
27 KiB

  1. // Copyright (c) 1999 Microsoft Corporation. All rights reserved.
  2. //
  3. // Implementation of Parser.
  4. //
  5. //#define LIMITEDVBSCRIPT_LOGPARSER // ��
  6. #include "stdinc.h"
  7. #include "enginc.h"
  8. #include "engparse.h"
  9. #include "engerror.h"
  10. #include "engexpr.h"
  11. #ifdef LIMITEDVBSCRIPT_LOGPARSER
  12. #include "englog.h"
  13. #endif
  14. // initial sizes for hash tables
  15. // �� tune these values?
  16. const int g_cInitialRoutineLookup = 32;
  17. const int g_cInitialGlobalsLookup = 32;
  18. const int g_cInitialVarRefsLookup = 64;
  19. const int g_cInitialAttrsLookup = 32;
  20. const int g_cInitialLocalsLookup = 32;
  21. // The parser will just hold weak references to the passed interfaces because we know that the parser is fully
  22. // contained in the lifetime of its parent -- CAudioVBScriptEngine.
  23. Parser::Parser(Lexer &lexer, Script &script, IActiveScriptSite *pActiveScriptSite, IDispatch *pGlobalDispatch)
  24. : m_lexer(lexer),
  25. m_plkuRoutines(NULL),
  26. m_plkuGlobals(NULL),
  27. m_plkuVarRefs(NULL),
  28. m_plkuNames(NULL),
  29. m_pActiveScriptSite(pActiveScriptSite),
  30. m_script(script),
  31. m_pGlobalDispatch(pGlobalDispatch)
  32. {
  33. m_plkuRoutines = new Lookup(&m_hr, m_script.strings, g_cInitialRoutineLookup);
  34. if (!m_plkuRoutines)
  35. m_hr = E_OUTOFMEMORY;
  36. if (FAILED(m_hr))
  37. return;
  38. m_plkuGlobals = new Lookup(&m_hr, m_script.strings, g_cInitialGlobalsLookup);
  39. if (!m_plkuGlobals)
  40. m_hr = E_OUTOFMEMORY;
  41. if (FAILED(m_hr))
  42. return;
  43. m_plkuVarRefs = new Lookup(&m_hr, m_script.strings, g_cInitialVarRefsLookup);
  44. if (!m_plkuVarRefs)
  45. m_hr = E_OUTOFMEMORY;
  46. if (FAILED(m_hr))
  47. return;
  48. m_plkuNames = new Lookup(&m_hr, m_script.strings, g_cInitialAttrsLookup);
  49. if (!m_plkuNames)
  50. m_hr = E_OUTOFMEMORY;
  51. if (FAILED(m_hr))
  52. return;
  53. // Set up the built in constants as the first global variables.
  54. for (int i = 0; i < g_cBuiltInConstants; ++i)
  55. {
  56. Variables::index iSlot = m_script.globals.Next();
  57. Strings::index iStr;
  58. m_hr = m_plkuGlobals->FindOrAdd(g_rgszBuiltInConstants[i], iSlot, iStr);
  59. if (FAILED(m_hr))
  60. return;
  61. assert(m_hr == S_FALSE);
  62. Variable variable(iStr);
  63. m_hr = m_script.globals.Add(variable);
  64. if (FAILED(m_hr))
  65. return;
  66. }
  67. assert(m_script.globals.Next() == g_cBuiltInConstants); // they occupy the slots 0 to g_cBuiltInConstants - 1
  68. ParseScript();
  69. }
  70. Parser::~Parser()
  71. {
  72. delete m_plkuRoutines;
  73. delete m_plkuGlobals;
  74. delete m_plkuVarRefs;
  75. delete m_plkuNames;
  76. }
  77. // top-level loop that parses the script
  78. void
  79. Parser::ParseScript()
  80. {
  81. if (FAILED(m_hr))
  82. {
  83. assert(false);
  84. return;
  85. }
  86. // make sure the lexer has a token to start with
  87. if (!m_lexer)
  88. {
  89. if (m_lexer.error_num())
  90. Error(PARSEERR_LexerError);
  91. return;
  92. }
  93. if (Skip(TOKEN_linebreak) || !m_lexer)
  94. return;
  95. // parse subs and dims...
  96. bool fSub = false; // used to ensure that add Dim statements occur before all Sub statements
  97. for (;;)
  98. {
  99. if (m_lexer == TOKEN_dim)
  100. {
  101. if (fSub)
  102. Error(PARSEERR_DimAfterSub);
  103. else
  104. ParseDim();
  105. }
  106. else if (m_lexer == TOKEN_sub)
  107. {
  108. fSub = true;
  109. ParseSub();
  110. }
  111. else
  112. {
  113. Error(PARSEERR_ExpectedSubOrDim);
  114. }
  115. if (FAILED(m_hr) || !m_lexer)
  116. return;
  117. // must be followed by line break
  118. if (Expect(TOKEN_linebreak, PARSEERR_ExpectedLineBreak) || !m_lexer)
  119. return;
  120. }
  121. }
  122. // pre: at sub
  123. // post: beyond end sub
  124. void
  125. Parser::ParseSub()
  126. {
  127. if (FAILED(m_hr))
  128. {
  129. assert(false);
  130. return;
  131. }
  132. // sub followed by identifier
  133. assert(m_lexer == TOKEN_sub);
  134. if (ExpectNext(TOKEN_identifier, PARSEERR_ExpectedIdentifier))
  135. return;
  136. const char *pszName = m_lexer.identifier_name();
  137. // check if there's already a variable with this name
  138. Variables::index iVar;
  139. Strings::index iStrVar;
  140. if (m_plkuGlobals->Find(pszName, iVar, iStrVar))
  141. {
  142. Error(PARSEERR_DuplicateVariable);
  143. return;
  144. }
  145. Routines::index iSlot = m_script.routines.Next();
  146. Strings::index iStr;
  147. m_hr = m_plkuRoutines->FindOrAdd(pszName, iSlot, iStr);
  148. if (FAILED(m_hr))
  149. return;
  150. if (m_hr == S_FALSE)
  151. {
  152. Routine routine(iStr);
  153. m_hr = m_script.routines.Add(routine);
  154. if (FAILED(m_hr))
  155. return;
  156. }
  157. else
  158. {
  159. Error(PARSEERR_DuplicateRoutine);
  160. return;
  161. }
  162. if (ExpectNext(TOKEN_linebreak, PARSEERR_ExpectedLineBreak))
  163. return;
  164. Lookup lkuLocals(&m_hr, m_script.strings, g_cInitialLocalsLookup);
  165. if (FAILED(m_hr))
  166. return;
  167. m_script.routines[iSlot].istmtBody = ParseStatements(iSlot, lkuLocals);
  168. if (FAILED(m_hr))
  169. return;
  170. if (m_lexer != TOKEN_end)
  171. {
  172. if (m_lexer == TOKEN_dim)
  173. {
  174. // AudioVBScript disallows dim statements except at the top of the script.
  175. // If one was found in the sub, users should receive the more specific error message.
  176. Error(PARSEERR_DimAfterSub);
  177. }
  178. else
  179. {
  180. Error(PARSEERR_ExpectedEndSub);
  181. }
  182. return;
  183. }
  184. if (Advance())
  185. return;
  186. if (Expect(TOKEN_sub, PARSEERR_ExpectedSub))
  187. return;
  188. #ifdef LIMITEDVBSCRIPT_LOGPARSER
  189. LogRoutine(m_script, iSlot);
  190. #endif
  191. }
  192. // pre: at dim
  193. // post: beyond dim <identifier>
  194. void
  195. Parser::ParseDim()
  196. {
  197. if (FAILED(m_hr))
  198. {
  199. assert(false);
  200. return;
  201. }
  202. assert(m_lexer == TOKEN_dim);
  203. if (ExpectNext(TOKEN_identifier, PARSEERR_ExpectedIdentifier))
  204. return;
  205. Variables::index iSlot = m_script.globals.Next();
  206. Strings::index iStr;
  207. m_hr = m_plkuGlobals->FindOrAdd(m_lexer.identifier_name(), iSlot, iStr);
  208. if (FAILED(m_hr))
  209. return;
  210. if (m_hr == S_FALSE)
  211. {
  212. Variable variable(iStr);
  213. m_hr = m_script.globals.Add(variable);
  214. if (FAILED(m_hr))
  215. return;
  216. }
  217. else
  218. {
  219. Error(PARSEERR_DuplicateVariable);
  220. return;
  221. }
  222. if (Advance())
  223. return;
  224. }
  225. // pre: at line break preceding the expected statements
  226. // post: after end of line at token that isn't the start of another statement
  227. Statements::index
  228. Parser::ParseStatements(Routines::index irtnContext, Lookup &lkuLocals)
  229. {
  230. if (FAILED(m_hr))
  231. {
  232. assert(false);
  233. return 0;
  234. }
  235. assert(m_lexer == TOKEN_linebreak);
  236. if (Skip(TOKEN_linebreak))
  237. return 0;
  238. Statements::index istmtStart = m_script.statements.Next();
  239. for (;;) // ever
  240. {
  241. bool fBreakLoop = false;
  242. switch (m_lexer)
  243. {
  244. case TOKEN_if:
  245. ParseIf(irtnContext, lkuLocals);
  246. break;
  247. case TOKEN_set:
  248. if (Advance())
  249. return 0;
  250. ParseAssignmentOrCall(irtnContext, lkuLocals, true, false);
  251. break;
  252. case TOKEN_call:
  253. if (Advance())
  254. return 0;
  255. ParseAssignmentOrCall(irtnContext, lkuLocals, false, true);
  256. break;
  257. case TOKEN_identifier:
  258. case TOKEN_identifierdot:
  259. ParseAssignmentOrCall(irtnContext, lkuLocals, false, false);
  260. break;
  261. default:
  262. fBreakLoop = true;
  263. break;
  264. }
  265. if (fBreakLoop)
  266. break;
  267. if (FAILED(m_hr) || Expect(TOKEN_linebreak, PARSEERR_ExpectedLineBreak))
  268. return 0;
  269. }
  270. m_hr = m_script.statements.Add(Statement(Statement::cons_end(), 0));
  271. if (FAILED(m_hr))
  272. return 0;
  273. return istmtStart;
  274. }
  275. // pre: at identifier or identifierdot (ambiguous as to whether this is going to be an assignment "x = 1" or call "x(1)")
  276. // post: at line break beyond statement
  277. void
  278. Parser::ParseAssignmentOrCall(Routines::index irtnContext, Lookup &lkuLocals, bool fSet, bool fCall)
  279. {
  280. assert(!(fSet && fCall));
  281. if (FAILED(m_hr))
  282. {
  283. assert(false);
  284. return;
  285. }
  286. assert(m_lexer == TOKEN_identifier || m_lexer == TOKEN_identifierdot);
  287. NameReference nref;
  288. ParseNameReference(irtnContext, lkuLocals, nref);
  289. if (FAILED(m_hr))
  290. return;
  291. if (fCall ? ExpectNext(TOKEN_lparen, PARSEERR_ExpectedLparen) : Advance())
  292. return;
  293. if (m_lexer == TOKEN_op_eq)
  294. {
  295. VariableReferences::index ivarrefLHS = VariableReferenceFromNameReference(irtnContext, lkuLocals, nref);
  296. if (FAILED(m_hr))
  297. return;
  298. Assignments::index iasgn = ParseAssignment(irtnContext, lkuLocals, fSet, ivarrefLHS);
  299. if (FAILED(m_hr))
  300. return;
  301. m_hr = m_script.statements.Add(Statement(Statement::cons_asgn(), iasgn, m_lexer.line()));
  302. if (FAILED(m_hr))
  303. return;
  304. }
  305. else
  306. {
  307. if (fSet)
  308. {
  309. Error(PARSEERR_ExpectedEq);
  310. return;
  311. }
  312. // add the call and its statement
  313. Calls::index icall = CallFromNameReference(irtnContext, lkuLocals, nref, fCall);
  314. if (FAILED(m_hr))
  315. return;
  316. m_hr = m_script.statements.Add(Statement(Statement::cons_call(), icall, m_lexer.line()));
  317. if (FAILED(m_hr))
  318. return;
  319. }
  320. }
  321. // pre: at identifierdot
  322. // post: at identifier
  323. VariableReferences::index Parser::ParseDottedVariableReference(Routines::index irtnContext, Lookup &lkuLocals)
  324. {
  325. return VariableReferenceInternal(irtnContext, lkuLocals, NULL);
  326. }
  327. // pre: at =
  328. // post: at line break beyond statement
  329. Assignments::index
  330. Parser::ParseAssignment(Routines::index irtnContext, Lookup &lkuLocals, bool fSet, VariableReferences::index ivarrefLHS)
  331. {
  332. if (FAILED(m_hr))
  333. {
  334. assert(false);
  335. return 0;
  336. }
  337. // make sure they're not trying to assign to one of the built in constants
  338. VariableReference &vr = m_script.varrefs[ivarrefLHS];
  339. if (vr.k == VariableReference::_global && vr.ivar < g_cBuiltInConstants)
  340. {
  341. Error(PARSEERR_AssignedToBuiltInConstant);
  342. return 0;
  343. }
  344. assert(m_lexer == TOKEN_op_eq);
  345. if (Skip(TOKEN_op_eq))
  346. return 0;
  347. ExprBlocks::index iexprRHS = ParseExpression(irtnContext, lkuLocals, NULL, false, false);
  348. if (FAILED(m_hr))
  349. return 0;
  350. if (m_lexer != TOKEN_linebreak)
  351. {
  352. Error(PARSEERR_InvalidExprLineBreak);
  353. return 0;
  354. }
  355. Assignments::index iasgn = m_script.asgns.Next();
  356. m_hr = m_script.asgns.Add(Assignment(fSet, ivarrefLHS, iexprRHS));
  357. if (FAILED(m_hr))
  358. return 0;
  359. return iasgn;
  360. }
  361. // pre: at if
  362. // post: beyond end if
  363. void
  364. Parser::ParseIf(Routines::index irtnContext, Lookup &lkuLocals)
  365. {
  366. if (FAILED(m_hr))
  367. {
  368. assert(false);
  369. return;
  370. }
  371. // Temporarily place if blocks in separate slots.
  372. // After completing the whole if statement then we'll append them to m_script.ifs.
  373. // This is necessary because we could end up recursively parsing nested ifs and the parent if
  374. // can't have its if blocks interleaved with its children.
  375. IfBlocks ifsTemp;
  376. // add the main if statement, which needs to go on first before we add the statements from its body
  377. Statements::index istmtIf = m_script.statements.Next();
  378. m_hr = m_script.statements.Add(Statement(Statement::cons_if(), m_lexer.line()));
  379. if (FAILED(m_hr))
  380. return;
  381. bool fFirst = true;
  382. do
  383. {
  384. // determine what part of the if (if/elseif/else) we're dealing with
  385. bool fCondition = true;
  386. if (fFirst)
  387. {
  388. assert(m_lexer == TOKEN_if);
  389. fFirst = false;
  390. }
  391. else
  392. {
  393. assert(m_lexer == TOKEN_elseif || m_lexer == TOKEN_else);
  394. if (m_lexer == TOKEN_else)
  395. fCondition = false;
  396. }
  397. if (Advance())
  398. return;
  399. ExprBlocks::index iexprIf = 0;
  400. if (fCondition)
  401. {
  402. // read the condition followed by then
  403. iexprIf = ParseExpression(irtnContext, lkuLocals, NULL, false, false);
  404. if (FAILED(m_hr))
  405. return;
  406. if (Expect(TOKEN_then, PARSEERR_ExpectedThen))
  407. return;
  408. }
  409. // line break
  410. if (m_lexer != TOKEN_linebreak)
  411. {
  412. Error(!fCondition && m_lexer == TOKEN_if ? PARSEERR_IntendedElseIf : PARSEERR_ExpectedLineBreak);
  413. return;
  414. }
  415. // statements
  416. Statements::index istmtIfBody = ParseStatements(irtnContext, lkuLocals);
  417. if (FAILED(m_hr))
  418. return;
  419. // add the if block
  420. if (fCondition)
  421. m_hr = ifsTemp.Add(IfBlock(iexprIf, istmtIfBody));
  422. else
  423. m_hr = ifsTemp.Add(IfBlock(istmtIfBody));
  424. if (FAILED(m_hr))
  425. return;
  426. }
  427. while (m_lexer != TOKEN_end);
  428. if (Advance())
  429. return;
  430. if (Expect(TOKEN_if, PARSEERR_ExpectedIf))
  431. return;
  432. // copy the temp if blocks into the script's real blocks
  433. IfBlocks::index iifIf = m_script.ifs.Next();
  434. IfBlocks::index iifTempNext = ifsTemp.Next();
  435. for (IfBlocks::index iifTemp = 0; iifTemp < iifTempNext; ++iifTemp)
  436. {
  437. m_hr = m_script.ifs.Add(ifsTemp[iifTemp]);
  438. if (FAILED(m_hr))
  439. return;
  440. }
  441. // terminate the if blocks
  442. m_hr = m_script.ifs.Add(IfBlock());
  443. if (FAILED(m_hr))
  444. return;
  445. // now set the main if statement's if blocks and tail
  446. Statement &stmtIf = m_script.statements[istmtIf];
  447. stmtIf.iif = iifIf;
  448. stmtIf.istmtIfTail = m_script.statements.Next();
  449. }
  450. // pre: none (at location where expression is exprected)
  451. // post: beyond last token that is part of a legal expression
  452. ExprBlocks::index
  453. Parser::ParseExpression(Routines::index irtnContext, Lookup &lkuLocals, ExprBlocks *peblocks, bool fAllowRparenAtEnd, bool fDetectSpecialErrorForSubCallWithParen)
  454. {
  455. // if peblocks is non-null then the expression is appended there
  456. // otherwise it goes onto the blocks in the script
  457. ExprBlocks &eblocks = peblocks ? *peblocks : m_script.exprs;
  458. Expression expr(m_script, m_stack, peblocks);
  459. if (m_lexer == TOKEN_stringliteral)
  460. {
  461. // a string literal is the only element of an expression
  462. Strings::index iStr;
  463. m_hr = m_script.strings.Add(m_lexer.stringliteral_text(), iStr);
  464. if (FAILED(m_hr))
  465. return 0;
  466. Values::index ival = m_script.vals.Next();
  467. m_hr = m_script.vals.Add(Value(Value::cons_strvalue(), iStr));
  468. if (FAILED(m_hr))
  469. return 0;
  470. m_hr = expr.Add(ExprBlock(ExprBlock::cons_val(), ival));
  471. if (FAILED(m_hr))
  472. return 0;
  473. if (Advance())
  474. return 0;
  475. }
  476. else
  477. {
  478. // The format of an expression is:
  479. // 1) zero or more unary operators
  480. // 2) a value
  481. // 3) either end here or have a binary operator and go back to step 1
  482. // Oh yeah? What about parentheses?
  483. // * If a left paren is encountered in step 2, we increase a paren count and go back to stage 1.
  484. // * In stage three, if there is paren count then a right paren is expected instead of ending.
  485. // After a matching right paren, we decrease the paren count and pop back to stage 3.
  486. UINT cParens = 0;
  487. for (;;)
  488. {
  489. // stage 1
  490. while (CheckOperatorType(m_lexer, false, true, false, false))
  491. {
  492. // replace minus with sub so that the expression evaluator can identify unary (negation) vs binary (subtraction)
  493. m_hr = expr.Add(ExprBlock(ExprBlock::cons_op(), m_lexer == TOKEN_op_minus ? TOKEN_sub : m_lexer));
  494. if (FAILED(m_hr))
  495. return 0;
  496. if (Advance())
  497. return 0;
  498. }
  499. // stage 2
  500. if (m_lexer == TOKEN_lparen)
  501. {
  502. m_hr = expr.Add(ExprBlock(ExprBlock::cons_op(), m_lexer));
  503. if (FAILED(m_hr))
  504. return 0;
  505. if (Advance())
  506. return 0;
  507. ++cParens;
  508. continue;
  509. }
  510. else if (m_lexer == TOKEN_identifier || m_lexer == TOKEN_identifierdot)
  511. {
  512. NameReference nref;
  513. ParseNameReference(irtnContext, lkuLocals, nref);
  514. if (FAILED(m_hr))
  515. return 0;
  516. if (Advance())
  517. return 0;
  518. if (m_lexer == TOKEN_lparen)
  519. {
  520. // add the call and the expression block
  521. Calls::index icall = CallFromNameReference(irtnContext, lkuLocals, nref, true);
  522. m_hr = expr.Add(ExprBlock(ExprBlock::cons_call(), icall));
  523. if (FAILED(m_hr))
  524. return 0;
  525. }
  526. else
  527. {
  528. VariableReferences::index ivarref = VariableReferenceFromNameReference(irtnContext, lkuLocals, nref);
  529. if (FAILED(m_hr))
  530. return 0;
  531. Values::index ival = m_script.vals.Next();
  532. m_hr = m_script.vals.Add(Value(Value::cons_varref(), ivarref));
  533. if (FAILED(m_hr))
  534. return 0;
  535. m_hr = expr.Add(ExprBlock(ExprBlock::cons_val(), ival));
  536. if (FAILED(m_hr))
  537. return 0;
  538. }
  539. }
  540. else if (m_lexer == TOKEN_numericliteral)
  541. {
  542. Values::index ival = m_script.vals.Next();
  543. m_hr = m_script.vals.Add(Value(Value::cons_numvalue(), m_lexer.numericliteral_val()));
  544. if (FAILED(m_hr))
  545. return 0;
  546. m_hr = expr.Add(ExprBlock(ExprBlock::cons_val(), ival));
  547. if (FAILED(m_hr))
  548. return 0;
  549. if (Advance())
  550. return 0;
  551. }
  552. else
  553. {
  554. Error(m_lexer == TOKEN_stringliteral ? PARSEERR_StringInExprBlock: PARSEERR_ExpectedExprValue);
  555. return 0;
  556. }
  557. // stage 3
  558. while (m_lexer == TOKEN_rparen)
  559. {
  560. if (cParens == 0)
  561. {
  562. if (fAllowRparenAtEnd)
  563. break;
  564. Error(PARSEERR_UnexpectedRparen);
  565. return 0;
  566. }
  567. m_hr = expr.Add(ExprBlock(ExprBlock::cons_op(), m_lexer));
  568. if (FAILED(m_hr))
  569. return 0;
  570. if (Advance())
  571. return 0;
  572. --cParens;
  573. }
  574. if (!CheckOperatorType(m_lexer, false, false, true, false))
  575. {
  576. if (cParens > 0)
  577. {
  578. if (fDetectSpecialErrorForSubCallWithParen && cParens == 1 && m_lexer == TOKEN_comma)
  579. {
  580. // This special error is needed to give a helpful error message in cases like the following:
  581. // Segment1.Play(IsSecondary, AP)
  582. // Here the user has accidentally called a sub using parentheses when they shouldn't.
  583. Error(PARSEERR_ParensUsedCallingSub);
  584. }
  585. else
  586. {
  587. Error(PARSEERR_ExpectedRparen);
  588. }
  589. return 0;
  590. }
  591. // *** this is the only successful exit point from the loop ***
  592. break;
  593. }
  594. expr.Add(ExprBlock(ExprBlock::cons_op(), m_lexer));
  595. if (Advance())
  596. return 0;
  597. }
  598. }
  599. ExprBlocks::index iexprStart = eblocks.Next();
  600. m_hr = expr.Generate();
  601. if (FAILED(m_hr))
  602. return 0;
  603. return iexprStart;
  604. }
  605. // if fParenthesized is true
  606. // pre: at lparen
  607. // post: beyond rparen
  608. // if fParenthesized is false
  609. // pre: none (at location where expression for first parameter is expected)
  610. // post: at linebreak
  611. ExprBlocks::index Parser::ParseParameters(Routines::index irtnContext, Lookup &lkuLocals, bool fParenthesized)
  612. {
  613. if (FAILED(m_hr))
  614. {
  615. assert(false);
  616. return 0;
  617. }
  618. if (fParenthesized)
  619. {
  620. assert(m_lexer == TOKEN_lparen);
  621. if (Advance())
  622. return 0;
  623. }
  624. // Temporarily place expression blocks in separate slots.
  625. // After completing the parameters then we'll append them to m_script.exprs.
  626. // This is necessary because we could end up recursively parsing nested calls inside an expression and
  627. // the parent parameters can't have their blocks interleaved with subexpression calls.
  628. ExprBlocks exprsTemp;
  629. Token tStop = fParenthesized ? TOKEN_rparen : TOKEN_linebreak;
  630. ParseErr perrExpectedFinish = fParenthesized ? PARSEERR_InvalidExprRparen : PARSEERR_InvalidExprLineBreak;
  631. bool fFirstParam = true;
  632. while (m_lexer != tStop)
  633. {
  634. if (!fFirstParam)
  635. {
  636. if (Expect(TOKEN_comma, perrExpectedFinish) || !m_lexer)
  637. return 0;
  638. }
  639. if (m_lexer == TOKEN_comma)
  640. {
  641. // This parameter is omitted. Save it as an empty expression.
  642. // Example:
  643. // MySong.Play , , , OldPlayingSong
  644. // There the first three parameters are omitted.
  645. m_hr = exprsTemp.Add(ExprBlock(ExprBlock::cons_omitted()));
  646. if (FAILED(m_hr))
  647. return 0;
  648. m_hr = exprsTemp.Add(ExprBlock(ExprBlock::cons_end()));
  649. if (FAILED(m_hr))
  650. return 0;
  651. }
  652. else
  653. {
  654. // The last parameter is set to true to detect a special error when we're calling a sub (!fParenthesized) and when this is
  655. // the first parameter to that sub (fFirstParam). This will detect a comma and give a better error message in cases like
  656. // the following:
  657. // Segment1.Play(IsSecondary, AP)
  658. // Here the user has accidentally called a sub using parameters when they shouldn't.
  659. ExprBlocks::index iexpr = ParseExpression(irtnContext, lkuLocals, &exprsTemp, fParenthesized, !fParenthesized && fFirstParam);
  660. if (FAILED(m_hr))
  661. return 0;
  662. }
  663. fFirstParam = false;
  664. }
  665. if (fParenthesized)
  666. {
  667. assert(m_lexer == TOKEN_rparen);
  668. if (Advance())
  669. return 0;
  670. }
  671. else
  672. {
  673. assert(m_lexer == TOKEN_linebreak);
  674. }
  675. // terminate the parameters
  676. m_hr = exprsTemp.Add(ExprBlock(ExprBlock::cons_end()));
  677. if (FAILED(m_hr))
  678. return 0;
  679. // copy from the temporary blocks into the script
  680. ExprBlocks::index iexprFirstInScript = m_script.exprs.Next();
  681. ExprBlocks::index iexprLastInTemp = exprsTemp.Next();
  682. for (ExprBlocks::index iexpr = 0; iexpr < iexprLastInTemp; ++iexpr)
  683. {
  684. m_hr = m_script.exprs.Add(exprsTemp[iexpr]);
  685. if (FAILED(m_hr))
  686. return 0;
  687. }
  688. return iexprFirstInScript;
  689. }
  690. void Parser::ParseNameReference(Routines::index irtnContext, Lookup &lkuLocals, NameReference &nref)
  691. {
  692. nref.fSingleItem = m_lexer == TOKEN_identifier;
  693. nref.ivarrefMultiple = 0;
  694. if (nref.fSingleItem)
  695. {
  696. nref.strSingle = m_lexer.identifier_name();
  697. if (!nref.strSingle)
  698. {
  699. m_hr = E_OUTOFMEMORY;
  700. }
  701. }
  702. else
  703. {
  704. nref.ivarrefMultiple = ParseDottedVariableReference(irtnContext, lkuLocals);
  705. }
  706. }
  707. VariableReferences::index Parser::VariableReferenceFromNameReference(Routines::index irtnContext, Lookup &lkuLocals, const NameReference &nref)
  708. {
  709. VariableReferences::index ivarref =
  710. nref.fSingleItem
  711. ? VariableReferenceInternal(irtnContext, lkuLocals, nref.strSingle)
  712. : nref.ivarrefMultiple;
  713. return ivarref;
  714. }
  715. Calls::index Parser::CallFromNameReference(Routines::index irtnContext, Lookup &lkuLocals, const NameReference &nref, bool fParametersParenthesized)
  716. {
  717. Call c;
  718. if (nref.fSingleItem)
  719. {
  720. // resolve the name in our temporary parsing name table
  721. Names_Parse::index iSlotName = m_names.Next();
  722. Strings::index iStrName = 0;
  723. m_hr = m_plkuNames->FindOrAdd(nref.strSingle, iSlotName, iStrName);
  724. if (FAILED(m_hr))
  725. return 0;
  726. if (m_hr == S_FALSE)
  727. {
  728. m_hr = m_names.Add(Name_Parse(iStrName));
  729. if (FAILED(m_hr))
  730. return 0;
  731. }
  732. c.k = Call::_global;
  733. c.istrname = iStrName;
  734. }
  735. else
  736. {
  737. c.k = Call::_dereferenced;
  738. c.ivarref = nref.ivarrefMultiple;
  739. }
  740. c.iexprParams = ParseParameters(irtnContext, lkuLocals, fParametersParenthesized);
  741. if (FAILED(m_hr))
  742. return 0;
  743. // add the call
  744. Calls::index icall = m_script.calls.Next();
  745. m_hr = m_script.calls.Add(c);
  746. if (FAILED(m_hr))
  747. return 0;
  748. return icall;
  749. }
  750. VariableReferences::index Parser::VariableReferenceInternal(Routines::index irtnContext, Lookup &lkuLocals, const char *pszName)
  751. {
  752. if (FAILED(m_hr))
  753. {
  754. assert(false);
  755. return 0;
  756. }
  757. assert(pszName || m_lexer == TOKEN_identifierdot);
  758. ReferenceNames::index irname = m_script.rnames.Next();
  759. // resolve the first item, which is a variable in the script
  760. bool fLocal = false;
  761. Variables::index ivarBase;
  762. Strings::index iStrBase;
  763. const char *pszBase = pszName ? pszName : m_lexer.identifier_name();
  764. // check if there's already a routine with this name
  765. Variables::index iRtn;
  766. Strings::index iStrRtn;
  767. if (m_plkuRoutines->Find(pszBase, iRtn, iStrRtn))
  768. {
  769. Error(PARSEERR_ExpectedVariableButRoutineFound);
  770. return 0;
  771. }
  772. // try the globals
  773. if (!m_plkuGlobals->Find(pszBase, ivarBase, iStrBase))
  774. {
  775. // try the locals
  776. if (lkuLocals.Find(pszBase, ivarBase, iStrBase))
  777. {
  778. fLocal = true;
  779. }
  780. else
  781. {
  782. // try the global dispatch
  783. DISPID dispid = GetDispID(m_pGlobalDispatch, pszBase);
  784. if (dispid != DISPID_UNKNOWN)
  785. {
  786. // add it as a global
  787. // �� Possible performance optimization. Oops. An unintended consequence is that the script
  788. // will reserve a variant slot for this as a global variable at runtime. Could do something
  789. // so save this memory if that's a problem.
  790. ivarBase = m_script.globals.Next();
  791. m_hr = m_plkuGlobals->FindOrAdd(pszBase, ivarBase, iStrBase);
  792. if (FAILED(m_hr))
  793. return 0;
  794. assert(m_hr == S_FALSE); // we already tried Find, above so must be added
  795. Variable variable(iStrBase, dispid);
  796. m_hr = m_script.globals.Add(variable);
  797. if (FAILED(m_hr))
  798. return 0;
  799. }
  800. else
  801. {
  802. // add to the locals
  803. fLocal = true;
  804. ivarBase = m_script.routines[irtnContext].ivarNextLocal;
  805. m_hr = lkuLocals.FindOrAdd(pszBase, ivarBase, iStrBase);
  806. if (FAILED(m_hr))
  807. return 0;
  808. assert(m_hr == S_FALSE); // we already tried Find, above so must be added
  809. assert(ivarBase == m_script.routines[irtnContext].ivarNextLocal);
  810. ++m_script.routines[irtnContext].ivarNextLocal;
  811. }
  812. }
  813. }
  814. // save the name
  815. m_hr = m_script.rnames.Add(ReferenceName(iStrBase));
  816. if (FAILED(m_hr))
  817. return 0;
  818. if (!pszName)
  819. {
  820. // remaining items are scoped outside the script, so just record their names
  821. do
  822. {
  823. // next is identifier or identifierdot
  824. if (Advance())
  825. return 0;
  826. if (m_lexer != TOKEN_identifier && m_lexer != TOKEN_identifierdot)
  827. {
  828. Error(PARSEERR_ExpectedIdentifier);
  829. return 0;
  830. }
  831. // resolve the name in our temporary parsing name table
  832. Names_Parse::index iSlotName = m_names.Next();
  833. Strings::index iStrName = 0;
  834. m_hr = m_plkuNames->FindOrAdd(m_lexer.identifier_name(), iSlotName, iStrName);
  835. if (FAILED(m_hr))
  836. return 0;
  837. if (m_hr == S_FALSE)
  838. {
  839. m_hr = m_names.Add(Name_Parse(iStrName));
  840. if (FAILED(m_hr))
  841. return 0;
  842. }
  843. // save the name
  844. m_hr = m_script.rnames.Add(ReferenceName(iStrName));
  845. if (FAILED(m_hr))
  846. return 0;
  847. }
  848. while (m_lexer != TOKEN_identifier);
  849. }
  850. // terminates the rnames
  851. m_hr = m_script.rnames.Add(ReferenceName(-1));
  852. if (FAILED(m_hr))
  853. return 0;
  854. //
  855. // make and return the reference
  856. //
  857. VariableReferences::index ivarref = m_script.varrefs.Next();
  858. m_hr = m_script.varrefs.Add(VariableReference(fLocal ? VariableReference::_local : VariableReference::_global, irname, ivarBase));
  859. if (FAILED(m_hr))
  860. return 0;
  861. return ivarref;
  862. }
  863. void
  864. Parser::Error(ParseErr iErr)
  865. {
  866. static const char *s_rgpszErrorText[] =
  867. {
  868. "Unexpected error!", // shouldn't ever get this error
  869. "Expected Sub or Dim statement",
  870. "Expected identifier",
  871. "Expected line break",
  872. "Expected End Sub",
  873. "Expected Sub",
  874. "Expected statement",
  875. "Expected '('",
  876. "Expected '='",
  877. "All Dim statements must occur before all Sub statements",
  878. "Invalid expression or missing line break",
  879. "Invalid expression or missing Then",
  880. "Invalid expression or missing ')'",
  881. "Strings may not appear inside numerical expressions",
  882. "Invalid expression -- expected a number or variable",
  883. "A variable with this name already exists",
  884. "Another routine with this name already exists",
  885. "Invalid expression -- expected ')'",
  886. "Invalid expression -- encountered ')' without a preceding '('",
  887. "Expected Then",
  888. "Expected End If",
  889. "Expected If",
  890. "Expected line break or ElseIf should be a single word without space before If",
  891. "The values True, False, and Nothing are read-only and cannot be set",
  892. "Cannot use parentheses when calling a Sub",
  893. "Sub name used where variable was expected"
  894. };
  895. assert(ARRAY_SIZE(s_rgpszErrorText) == PARSEERR_Max);
  896. if (FAILED(m_hr))
  897. {
  898. // Something forgot to check m_hr. We were already in an error state previously so leave that error as is and assert.
  899. assert(false);
  900. return;
  901. }
  902. if (iErr < 0 || iErr > PARSEERR_Max)
  903. {
  904. assert(false);
  905. iErr = PARSEERR_LexerError;
  906. }
  907. m_hr = DMUS_E_AUDIOVBSCRIPT_SYNTAXERROR;
  908. // The error number should be passed as PARSEERR_LexerError if and only if the lexer is in an error state.
  909. // In this case we'll get our description from the lexer itelf. Otherwise we look it up in the table.
  910. assert((iErr == PARSEERR_LexerError) == (m_lexer == TOKEN_eof && m_lexer.error_num()));
  911. const char *pszErr = (m_lexer == TOKEN_eof && m_lexer.error_num()) ? m_lexer.error_descr() : s_rgpszErrorText[iErr];
  912. CActiveScriptError *perr = new CActiveScriptError(m_hr, m_lexer, pszErr);
  913. if (perr)
  914. {
  915. m_pActiveScriptSite->OnScriptError(perr);
  916. perr->Release();
  917. }
  918. }