taskjuggler 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (544) hide show
  1. data/CHANGELOG +2086 -0
  2. data/benchmarks/666tasks.tjp +3183 -0
  3. data/benchmarks/booking.tjp +14 -10
  4. data/doc/AppConfig.html +95 -73
  5. data/doc/Arguments.html +22 -2
  6. data/doc/CHANGELOG.html +2587 -0
  7. data/doc/COPYING.html +21 -1
  8. data/doc/Object.html +161 -122
  9. data/doc/README.html +21 -1
  10. data/doc/RuntimeConfig.html +26 -6
  11. data/doc/String.html +38 -18
  12. data/doc/StringIO.html +579 -0
  13. data/doc/TaskJuggler.html +251 -143
  14. data/doc/TaskJuggler/Account.html +26 -6
  15. data/doc/TaskJuggler/AccountAttribute.html +28 -8
  16. data/doc/TaskJuggler/AccountScenario.html +24 -4
  17. data/doc/TaskJuggler/Allocation.html +30 -10
  18. data/doc/TaskJuggler/AllocationAttribute.html +28 -8
  19. data/doc/TaskJuggler/AttributeBase.html +46 -26
  20. data/doc/TaskJuggler/AttributeDefinition.html +22 -2
  21. data/doc/TaskJuggler/BatchProcessor.html +40 -20
  22. data/doc/TaskJuggler/Booking.html +26 -6
  23. data/doc/TaskJuggler/BookingListAttribute.html +28 -8
  24. data/doc/TaskJuggler/BooleanAttribute.html +28 -8
  25. data/doc/TaskJuggler/CSVFile.html +308 -146
  26. data/doc/TaskJuggler/CellSettingPattern.html +22 -2
  27. data/doc/TaskJuggler/CellSettingPatternList.html +26 -6
  28. data/doc/TaskJuggler/Charge.html +26 -6
  29. data/doc/TaskJuggler/ChargeListAttribute.html +26 -6
  30. data/doc/TaskJuggler/ChargeSet.html +33 -13
  31. data/doc/TaskJuggler/ChargeSetListAttribute.html +28 -8
  32. data/doc/TaskJuggler/ColumnListAttribute.html +26 -6
  33. data/doc/TaskJuggler/ColumnTable.html +24 -4
  34. data/doc/TaskJuggler/Daemon.html +26 -6
  35. data/doc/TaskJuggler/DateAttribute.html +26 -6
  36. data/doc/TaskJuggler/DefinitionListAttribute.html +22 -2
  37. data/doc/TaskJuggler/DependencyListAttribute.html +28 -8
  38. data/doc/TaskJuggler/DurationAttribute.html +28 -8
  39. data/doc/TaskJuggler/FixnumAttribute.html +24 -4
  40. data/doc/TaskJuggler/FlagListAttribute.html +28 -8
  41. data/doc/TaskJuggler/FloatAttribute.html +26 -6
  42. data/doc/TaskJuggler/FormatListAttribute.html +24 -4
  43. data/doc/TaskJuggler/GanttChart.html +147 -126
  44. data/doc/TaskJuggler/GanttContainer.html +34 -14
  45. data/doc/TaskJuggler/GanttHeader.html +28 -8
  46. data/doc/TaskJuggler/GanttHeaderScaleItem.html +24 -4
  47. data/doc/TaskJuggler/GanttLine.html +38 -18
  48. data/doc/TaskJuggler/GanttLoadStack.html +26 -6
  49. data/doc/TaskJuggler/GanttMilestone.html +34 -14
  50. data/doc/TaskJuggler/GanttRouter.html +50 -30
  51. data/doc/TaskJuggler/GanttTaskBar.html +34 -14
  52. data/doc/TaskJuggler/HTMLDocument.html +26 -6
  53. data/doc/TaskJuggler/HTMLGraphics.html +30 -10
  54. data/doc/TaskJuggler/Interval.html +40 -22
  55. data/doc/TaskJuggler/IntervalListAttribute.html +28 -8
  56. data/doc/TaskJuggler/JobInfo.html +24 -4
  57. data/doc/TaskJuggler/Journal.html +226 -147
  58. data/doc/TaskJuggler/JournalEntry.html +22 -2
  59. data/doc/TaskJuggler/JournalEntryList.html +129 -112
  60. data/doc/TaskJuggler/KeywordArray.html +26 -6
  61. data/doc/TaskJuggler/KeywordDocumentation.html +46 -26
  62. data/doc/TaskJuggler/Limits.html +152 -123
  63. data/doc/TaskJuggler/Limits/Limit.html +149 -70
  64. data/doc/TaskJuggler/LimitsAttribute.html +36 -51
  65. data/doc/TaskJuggler/ListAttributeBase.html +24 -4
  66. data/doc/TaskJuggler/Log.html +50 -32
  67. data/doc/TaskJuggler/LogFile.html +37 -17
  68. data/doc/TaskJuggler/LogicalAttribute.html +53 -33
  69. data/doc/TaskJuggler/LogicalExpression.html +26 -6
  70. data/doc/TaskJuggler/LogicalExpressionAttribute.html +32 -12
  71. data/doc/TaskJuggler/LogicalFlag.html +42 -22
  72. data/doc/TaskJuggler/LogicalFunction.html +204 -140
  73. data/doc/TaskJuggler/LogicalOperation.html +135 -111
  74. data/doc/TaskJuggler/Macro.html +22 -2
  75. data/doc/TaskJuggler/MacroParser.html +32 -12
  76. data/doc/TaskJuggler/MacroTable.html +32 -12
  77. data/doc/TaskJuggler/ManagerResponsibilities.html +24 -4
  78. data/doc/TaskJuggler/ManagerStatusRecord.html +24 -4
  79. data/doc/TaskJuggler/Message.html +24 -4
  80. data/doc/TaskJuggler/MessageHandler.html +24 -4
  81. data/doc/TaskJuggler/Navigator.html +104 -71
  82. data/doc/TaskJuggler/NavigatorElement.html +32 -12
  83. data/doc/TaskJuggler/NikuProject.html +22 -2
  84. data/doc/TaskJuggler/NikuReport.html +310 -228
  85. data/doc/TaskJuggler/NikuResource.html +22 -2
  86. data/doc/TaskJuggler/NodeListAttribute.html +615 -0
  87. data/doc/TaskJuggler/OnShiftCache.html +32 -12
  88. data/doc/TaskJuggler/ProcessIntercom.html +205 -78
  89. data/doc/TaskJuggler/ProcessIntercomIface.html +26 -6
  90. data/doc/TaskJuggler/Project.html +708 -660
  91. data/doc/TaskJuggler/ProjectBroker.html +506 -304
  92. data/doc/TaskJuggler/ProjectBrokerIface.html +61 -41
  93. data/doc/TaskJuggler/ProjectFileParser.html +429 -373
  94. data/doc/TaskJuggler/ProjectFileScanner.html +1790 -0
  95. data/doc/TaskJuggler/ProjectRecord.html +80 -60
  96. data/doc/TaskJuggler/ProjectServer.html +312 -237
  97. data/doc/TaskJuggler/ProjectServerIface.html +101 -43
  98. data/doc/TaskJuggler/PropertyAttribute.html +32 -12
  99. data/doc/TaskJuggler/PropertyList.html +166 -145
  100. data/doc/TaskJuggler/PropertySet.html +254 -224
  101. data/doc/TaskJuggler/PropertyTreeNode.html +670 -536
  102. data/doc/TaskJuggler/Query.html +169 -148
  103. data/doc/TaskJuggler/RTFHandlers.html +622 -0
  104. data/doc/TaskJuggler/RTFNavigator.html +28 -8
  105. data/doc/TaskJuggler/RTFQuery.html +40 -20
  106. data/doc/TaskJuggler/RTFReport.html +62 -25
  107. data/doc/TaskJuggler/RTFReportLink.html +765 -0
  108. data/doc/TaskJuggler/RealFormat.html +26 -6
  109. data/doc/TaskJuggler/RealFormatAttribute.html +26 -6
  110. data/doc/TaskJuggler/ReferenceAttribute.html +59 -39
  111. data/doc/TaskJuggler/Report.html +402 -251
  112. data/doc/TaskJuggler/ReportBase.html +162 -137
  113. data/doc/TaskJuggler/ReportContext.html +112 -29
  114. data/doc/TaskJuggler/ReportServer.html +89 -64
  115. data/doc/TaskJuggler/ReportServerIface.html +75 -55
  116. data/doc/TaskJuggler/ReportServerRecord.html +54 -31
  117. data/doc/TaskJuggler/ReportServlet.html +980 -0
  118. data/doc/TaskJuggler/ReportTable.html +41 -21
  119. data/doc/TaskJuggler/ReportTableCell.html +214 -170
  120. data/doc/TaskJuggler/ReportTableColumn.html +30 -10
  121. data/doc/TaskJuggler/ReportTableLegend.html +36 -16
  122. data/doc/TaskJuggler/ReportTableLine.html +32 -12
  123. data/doc/TaskJuggler/Resource.html +99 -87
  124. data/doc/TaskJuggler/ResourceListAttribute.html +59 -39
  125. data/doc/TaskJuggler/ResourceListRE.html +47 -26
  126. data/doc/TaskJuggler/ResourceScenario.html +403 -437
  127. data/doc/TaskJuggler/RichText.html +26 -6
  128. data/doc/TaskJuggler/RichTextAttribute.html +50 -30
  129. data/doc/TaskJuggler/RichTextDocument.html +37 -17
  130. data/doc/TaskJuggler/RichTextElement.html +475 -413
  131. data/doc/TaskJuggler/RichTextException.html +22 -2
  132. data/doc/TaskJuggler/RichTextFunctionExample.html +28 -8
  133. data/doc/TaskJuggler/RichTextFunctionHandler.html +24 -4
  134. data/doc/TaskJuggler/RichTextImage.html +22 -2
  135. data/doc/TaskJuggler/RichTextIntermediate.html +38 -18
  136. data/doc/TaskJuggler/RichTextParser.html +56 -34
  137. data/doc/TaskJuggler/RichTextScanner.html +82 -61
  138. data/doc/TaskJuggler/RichTextSnip.html +34 -14
  139. data/doc/TaskJuggler/RichTextSyntaxRules.html +507 -353
  140. data/doc/TaskJuggler/Scenario.html +22 -2
  141. data/doc/TaskJuggler/ScenarioData.html +30 -46
  142. data/doc/TaskJuggler/ScenarioListAttribute.html +38 -18
  143. data/doc/TaskJuggler/Scoreboard.html +42 -22
  144. data/doc/TaskJuggler/SheetHandlerBase.html +40 -20
  145. data/doc/TaskJuggler/SheetReceiver.html +333 -295
  146. data/doc/TaskJuggler/SheetSender.html +253 -230
  147. data/doc/TaskJuggler/Shift.html +26 -6
  148. data/doc/TaskJuggler/ShiftAssignment.html +89 -73
  149. data/doc/TaskJuggler/ShiftAssignments.html +226 -234
  150. data/doc/TaskJuggler/ShiftAssignmentsAttribute.html +39 -54
  151. data/doc/TaskJuggler/ShiftScenario.html +28 -8
  152. data/doc/TaskJuggler/SortListAttribute.html +34 -14
  153. data/doc/TaskJuggler/SourceFileInfo.html +24 -4
  154. data/doc/TaskJuggler/StatusSheetReceiver.html +24 -3
  155. data/doc/TaskJuggler/StatusSheetReport.html +168 -153
  156. data/doc/TaskJuggler/StatusSheetSender.html +24 -3
  157. data/doc/TaskJuggler/StringAttribute.html +38 -18
  158. data/doc/TaskJuggler/SymbolAttribute.html +32 -12
  159. data/doc/TaskJuggler/SyntaxReference.html +40 -20
  160. data/doc/TaskJuggler/TOCEntry.html +26 -6
  161. data/doc/TaskJuggler/TSResourceRecord.html +22 -2
  162. data/doc/TaskJuggler/TSTaskRecord.html +22 -2
  163. data/doc/TaskJuggler/TableColumnDefinition.html +59 -22
  164. data/doc/TaskJuggler/TableOfContents.html +26 -6
  165. data/doc/TaskJuggler/TableReport.html +937 -904
  166. data/doc/TaskJuggler/Task.html +55 -36
  167. data/doc/TaskJuggler/TaskDependency.html +24 -4
  168. data/doc/TaskJuggler/TaskListAttribute.html +50 -30
  169. data/doc/TaskJuggler/TaskListRE.html +27 -7
  170. data/doc/TaskJuggler/TaskScenario.html +1273 -1153
  171. data/doc/TaskJuggler/TextFormatter.html +28 -8
  172. data/doc/TaskJuggler/TextParser.html +585 -338
  173. data/doc/TaskJuggler/TextParser/Pattern.html +54 -34
  174. data/doc/TaskJuggler/TextParser/Rule.html +95 -73
  175. data/doc/TaskJuggler/TextParser/StackElement.html +39 -17
  176. data/doc/TaskJuggler/TextParser/TextParserResultArray.html +24 -4
  177. data/doc/TaskJuggler/TextParser/TokenDoc.html +22 -2
  178. data/doc/TaskJuggler/TextReport.html +28 -8
  179. data/doc/TaskJuggler/TextScanner.html +400 -1404
  180. data/doc/TaskJuggler/TextScanner/BufferStreamHandle.html +28 -240
  181. data/doc/TaskJuggler/TextScanner/FileStreamHandle.html +37 -184
  182. data/doc/TaskJuggler/TextScanner/MacroStackEntry.html +682 -0
  183. data/doc/TaskJuggler/TextScanner/StreamHandle.html +342 -67
  184. data/doc/TaskJuggler/TimeSheet.html +48 -28
  185. data/doc/TaskJuggler/TimeSheetReceiver.html +24 -3
  186. data/doc/TaskJuggler/TimeSheetRecord.html +47 -27
  187. data/doc/TaskJuggler/TimeSheetReport.html +154 -133
  188. data/doc/TaskJuggler/TimeSheetSender.html +24 -3
  189. data/doc/TaskJuggler/TimeSheetSummary.html +137 -91
  190. data/doc/TaskJuggler/TimeSheets.html +26 -6
  191. data/doc/TaskJuggler/Tj3AppBase.html +85 -58
  192. data/doc/TaskJuggler/Tj3Client.html +292 -238
  193. data/doc/TaskJuggler/Tj3Daemon.html +159 -74
  194. data/doc/TaskJuggler/Tj3SheetAppBase.html +26 -6
  195. data/doc/TaskJuggler/Tj3SsReceiver.html +26 -6
  196. data/doc/TaskJuggler/Tj3SsSender.html +53 -26
  197. data/doc/TaskJuggler/Tj3TsReceiver.html +28 -7
  198. data/doc/TaskJuggler/Tj3TsSender.html +26 -6
  199. data/doc/TaskJuggler/Tj3TsSummary.html +26 -6
  200. data/doc/TaskJuggler/TjException.html +22 -2
  201. data/doc/TaskJuggler/TjTime.html +216 -160
  202. data/doc/TaskJuggler/TjpExample.html +34 -14
  203. data/doc/TaskJuggler/TjpExportRE.html +403 -407
  204. data/doc/TaskJuggler/TjpSyntaxRules.html +4805 -4408
  205. data/doc/TaskJuggler/URLParameter.html +649 -0
  206. data/doc/TaskJuggler/UserManual.html +42 -22
  207. data/doc/TaskJuggler/WebServer.html +702 -0
  208. data/doc/TaskJuggler/WorkingHours.html +38 -18
  209. data/doc/TaskJuggler/WorkingHoursAttribute.html +61 -41
  210. data/doc/TaskJuggler/XMLBlob.html +24 -4
  211. data/doc/TaskJuggler/XMLComment.html +24 -4
  212. data/doc/TaskJuggler/XMLDocument.html +30 -10
  213. data/doc/TaskJuggler/XMLElement.html +34 -14
  214. data/doc/TaskJuggler/XMLNamedText.html +22 -2
  215. data/doc/TaskJuggler/XMLText.html +24 -4
  216. data/doc/index.html +1841 -1666
  217. data/doc/lib/AccountScenario_rb.html +1 -1
  218. data/doc/lib/Account_rb.html +1 -1
  219. data/doc/lib/Allocation_rb.html +1 -1
  220. data/doc/lib/AppConfig_rb.html +2 -2
  221. data/doc/lib/AttributeBase_rb.html +1 -1
  222. data/doc/lib/AttributeDefinition_rb.html +1 -1
  223. data/doc/lib/Attributes_rb.html +2 -2
  224. data/doc/lib/BatchProcessor_rb.html +1 -1
  225. data/doc/lib/Booking_rb.html +1 -1
  226. data/doc/lib/ChargeSet_rb.html +1 -1
  227. data/doc/lib/Charge_rb.html +1 -1
  228. data/doc/lib/HTMLDocument_rb.html +1 -1
  229. data/doc/lib/Interval_rb.html +1 -1
  230. data/doc/lib/Journal_rb.html +2 -2
  231. data/doc/lib/KeywordArray_rb.html +1 -1
  232. data/doc/lib/KeywordDocumentation_rb.html +1 -1
  233. data/doc/lib/Limits_rb.html +2 -2
  234. data/doc/lib/LogFile_rb.html +2 -2
  235. data/doc/lib/Log_rb.html +1 -1
  236. data/doc/lib/LogicalExpression_rb.html +1 -1
  237. data/doc/lib/LogicalFunction_rb.html +2 -2
  238. data/doc/lib/LogicalOperation_rb.html +2 -2
  239. data/doc/lib/MacroParser_rb.html +1 -1
  240. data/doc/lib/MacroTable_rb.html +1 -1
  241. data/doc/lib/MessageHandler_rb.html +1 -1
  242. data/doc/lib/Message_rb.html +1 -1
  243. data/doc/lib/ProjectFileParser_rb.html +4 -8
  244. data/doc/lib/ProjectFileScanner_rb.html +67 -0
  245. data/doc/lib/Project_rb.html +2 -2
  246. data/doc/lib/PropertyList_rb.html +2 -2
  247. data/doc/lib/PropertySet_rb.html +2 -2
  248. data/doc/lib/PropertyTreeNode_rb.html +2 -2
  249. data/doc/lib/Query_rb.html +2 -2
  250. data/doc/lib/RTFHandlers_rb.html +73 -0
  251. data/doc/lib/RTFNavigator_rb.html +1 -1
  252. data/doc/lib/RTFQuery_rb.html +1 -1
  253. data/doc/lib/RTFReportLink_rb.html +71 -0
  254. data/doc/lib/RTFReport_rb.html +2 -2
  255. data/doc/lib/RealFormat_rb.html +1 -1
  256. data/doc/lib/ResourceScenario_rb.html +2 -2
  257. data/doc/lib/Resource_rb.html +2 -2
  258. data/doc/lib/RichTextDocument_rb.html +1 -1
  259. data/doc/lib/RichTextElement_rb.html +2 -2
  260. data/doc/lib/RichTextFunctionExample_rb.html +1 -1
  261. data/doc/lib/RichTextFunctionHandler_rb.html +1 -1
  262. data/doc/lib/RichTextParser_rb.html +2 -2
  263. data/doc/lib/RichTextScanner_rb.html +2 -2
  264. data/doc/lib/RichTextSnip_rb.html +1 -1
  265. data/doc/lib/RichTextSyntaxRules_rb.html +2 -2
  266. data/doc/lib/RichText_rb.html +1 -1
  267. data/doc/lib/RuntimeConfig_rb.html +1 -1
  268. data/doc/lib/ScenarioData_rb.html +2 -2
  269. data/doc/lib/Scenario_rb.html +1 -1
  270. data/doc/lib/Scoreboard_rb.html +1 -1
  271. data/doc/lib/SheetHandlerBase_rb.html +1 -1
  272. data/doc/lib/SheetReceiver_rb.html +2 -2
  273. data/doc/lib/SheetSender_rb.html +4 -2
  274. data/doc/lib/ShiftAssignments_rb.html +4 -2
  275. data/doc/lib/ShiftScenario_rb.html +1 -1
  276. data/doc/lib/Shift_rb.html +1 -1
  277. data/doc/lib/SourceFileInfo_rb.html +1 -1
  278. data/doc/lib/StatusSheetReceiver_rb.html +2 -2
  279. data/doc/lib/StatusSheetSender_rb.html +2 -2
  280. data/doc/lib/SyntaxReference_rb.html +1 -1
  281. data/doc/lib/TOCEntry_rb.html +1 -1
  282. data/doc/lib/TableColumnDefinition_rb.html +2 -2
  283. data/doc/lib/TableOfContents_rb.html +1 -1
  284. data/doc/lib/TaskDependency_rb.html +1 -1
  285. data/doc/lib/TaskJuggler_rb.html +2 -2
  286. data/doc/lib/TaskScenario_rb.html +2 -2
  287. data/doc/lib/Task_rb.html +2 -2
  288. data/doc/lib/TextFormatter_rb.html +1 -1
  289. data/doc/lib/TextParser/Pattern_rb.html +1 -1
  290. data/doc/lib/TextParser/Rule_rb.html +2 -2
  291. data/doc/lib/TextParser/StackElement_rb.html +2 -2
  292. data/doc/lib/TextParser/TokenDoc_rb.html +1 -1
  293. data/doc/lib/TextParser_rb.html +2 -2
  294. data/doc/lib/TextScanner_rb.html +6 -2
  295. data/doc/lib/TimeSheetReceiver_rb.html +2 -2
  296. data/doc/lib/TimeSheetSender_rb.html +2 -2
  297. data/doc/lib/TimeSheetSummary_rb.html +2 -2
  298. data/doc/lib/TimeSheets_rb.html +2 -2
  299. data/doc/lib/Tj3AppBase_rb.html +2 -2
  300. data/doc/lib/Tj3Config_rb.html +2 -2
  301. data/doc/lib/Tj3SheetAppBase_rb.html +2 -2
  302. data/doc/lib/TjException_rb.html +1 -1
  303. data/doc/lib/TjTime_rb.html +2 -2
  304. data/doc/lib/TjpExample_rb.html +1 -1
  305. data/doc/lib/TjpSyntaxRules_rb.html +2 -2
  306. data/doc/lib/URLParameter_rb.html +67 -0
  307. data/doc/lib/UTF8String_rb.html +1 -1
  308. data/doc/lib/UserManual_rb.html +1 -1
  309. data/doc/lib/WorkingHours_rb.html +1 -1
  310. data/doc/lib/XMLDocument_rb.html +2 -2
  311. data/doc/lib/XMLElement_rb.html +1 -1
  312. data/doc/lib/daemon/Daemon_rb.html +1 -1
  313. data/doc/lib/daemon/ProcessIntercom_rb.html +2 -2
  314. data/doc/lib/daemon/ProjectBroker_rb.html +4 -2
  315. data/doc/lib/daemon/ProjectServer_rb.html +2 -2
  316. data/doc/lib/daemon/ReportServer_rb.html +2 -2
  317. data/doc/lib/daemon/WebServer_rb.html +75 -0
  318. data/doc/lib/deep_copy_rb.html +2 -2
  319. data/doc/lib/reports/CSVFile_rb.html +2 -2
  320. data/doc/lib/reports/ColumnTable_rb.html +1 -1
  321. data/doc/lib/reports/GanttChart_rb.html +2 -2
  322. data/doc/lib/reports/GanttContainer_rb.html +1 -1
  323. data/doc/lib/reports/GanttHeaderScaleItem_rb.html +1 -1
  324. data/doc/lib/reports/GanttHeader_rb.html +1 -1
  325. data/doc/lib/reports/GanttLine_rb.html +1 -1
  326. data/doc/lib/reports/GanttLoadStack_rb.html +1 -1
  327. data/doc/lib/reports/GanttMilestone_rb.html +1 -1
  328. data/doc/lib/reports/GanttRouter_rb.html +1 -1
  329. data/doc/lib/reports/GanttTaskBar_rb.html +1 -1
  330. data/doc/lib/reports/HTMLGraphics_rb.html +1 -1
  331. data/doc/lib/reports/Navigator_rb.html +2 -2
  332. data/doc/lib/reports/NikuReport_rb.html +2 -2
  333. data/doc/lib/reports/ReportBase_rb.html +2 -2
  334. data/doc/lib/reports/ReportContext_rb.html +2 -2
  335. data/doc/lib/reports/ReportTableCell_rb.html +2 -2
  336. data/doc/lib/reports/ReportTableColumn_rb.html +1 -1
  337. data/doc/lib/reports/ReportTableLegend_rb.html +1 -1
  338. data/doc/lib/reports/ReportTableLine_rb.html +1 -1
  339. data/doc/lib/reports/ReportTable_rb.html +2 -2
  340. data/doc/lib/reports/Report_rb.html +2 -2
  341. data/doc/lib/reports/ResourceListRE_rb.html +2 -2
  342. data/doc/lib/reports/StatusSheetReport_rb.html +2 -2
  343. data/doc/lib/reports/TableReport_rb.html +2 -2
  344. data/doc/lib/reports/TaskListRE_rb.html +2 -2
  345. data/doc/lib/reports/TextReport_rb.html +1 -1
  346. data/doc/lib/reports/TimeSheetReport_rb.html +2 -2
  347. data/doc/lib/reports/TjpExportRE_rb.html +2 -2
  348. data/doc/lib/ruby-signal-bug_rb.html +1 -1
  349. data/doc/lib/taskjuggler3_rb.html +2 -2
  350. data/doc/lib/tj3client_rb.html +2 -2
  351. data/doc/lib/tj3d_rb.html +2 -2
  352. data/doc/lib/tj3man_rb.html +1 -1
  353. data/doc/lib/tj3ss_receiver_rb.html +1 -1
  354. data/doc/lib/tj3ss_sender_rb.html +2 -2
  355. data/doc/lib/tj3ts_receiver_rb.html +2 -2
  356. data/doc/lib/tj3ts_sender_rb.html +1 -1
  357. data/doc/lib/tj3ts_summary_rb.html +1 -1
  358. data/doc/rdoc.css +9 -4
  359. data/examples/tutorial.tjp +17 -15
  360. data/gem_spec.rb +3 -0
  361. data/lib/AppConfig.rb +3 -1
  362. data/lib/Attributes.rb +10 -8
  363. data/lib/Journal.rb +55 -20
  364. data/lib/Limits.rb +72 -46
  365. data/lib/LogFile.rb +3 -3
  366. data/lib/LogicalFunction.rb +19 -6
  367. data/lib/LogicalOperation.rb +5 -1
  368. data/lib/Project.rb +37 -9
  369. data/lib/ProjectFileParser.rb +25 -22
  370. data/lib/ProjectFileScanner.rb +365 -0
  371. data/lib/PropertyList.rb +2 -1
  372. data/lib/PropertySet.rb +14 -4
  373. data/lib/PropertyTreeNode.rb +42 -29
  374. data/lib/Query.rb +3 -1
  375. data/lib/RTFHandlers.rb +35 -0
  376. data/lib/RTFReport.rb +23 -6
  377. data/lib/RTFReportLink.rb +72 -0
  378. data/lib/Resource.rb +8 -16
  379. data/lib/ResourceScenario.rb +1 -22
  380. data/lib/RichTextElement.rb +42 -0
  381. data/lib/RichTextParser.rb +6 -4
  382. data/lib/RichTextScanner.rb +5 -5
  383. data/lib/RichTextSyntaxRules.rb +48 -10
  384. data/lib/ScenarioData.rb +5 -3
  385. data/lib/SheetReceiver.rb +24 -6
  386. data/lib/SheetSender.rb +17 -13
  387. data/lib/ShiftAssignments.rb +56 -52
  388. data/lib/StatusSheetReceiver.rb +1 -0
  389. data/lib/StatusSheetSender.rb +1 -0
  390. data/lib/TableColumnDefinition.rb +4 -2
  391. data/lib/Task.rb +8 -9
  392. data/lib/TaskJuggler.rb +21 -11
  393. data/lib/TaskScenario.rb +61 -26
  394. data/lib/TextParser.rb +268 -106
  395. data/lib/TextParser/Rule.rb +4 -2
  396. data/lib/TextParser/StackElement.rb +6 -4
  397. data/lib/TextScanner.rb +283 -700
  398. data/lib/TimeSheetReceiver.rb +1 -0
  399. data/lib/TimeSheetSender.rb +1 -0
  400. data/lib/TimeSheetSummary.rb +51 -26
  401. data/lib/TimeSheets.rb +1 -1
  402. data/lib/Tj3AppBase.rb +8 -1
  403. data/lib/Tj3Config.rb +1 -1
  404. data/lib/TjTime.rb +5 -0
  405. data/lib/TjpSyntaxRules.rb +360 -144
  406. data/lib/URLParameter.rb +30 -0
  407. data/lib/XMLDocument.rb +2 -2
  408. data/lib/daemon/ProcessIntercom.rb +50 -9
  409. data/lib/daemon/ProjectBroker.rb +63 -10
  410. data/lib/daemon/ProjectServer.rb +47 -16
  411. data/lib/daemon/ReportServer.rb +9 -4
  412. data/lib/daemon/WebServer.rb +204 -0
  413. data/lib/deep_copy.rb +4 -1
  414. data/lib/reports/CSVFile.rb +150 -66
  415. data/lib/reports/GanttChart.rb +2 -1
  416. data/lib/reports/Navigator.rb +18 -5
  417. data/lib/reports/NikuReport.rb +32 -2
  418. data/lib/reports/Report.rb +65 -37
  419. data/lib/reports/ReportBase.rb +14 -9
  420. data/lib/reports/ReportContext.rb +19 -4
  421. data/lib/reports/ReportTable.rb +2 -2
  422. data/lib/reports/ReportTableCell.rb +54 -30
  423. data/lib/reports/ResourceListRE.rb +4 -3
  424. data/lib/reports/StatusSheetReport.rb +8 -13
  425. data/lib/reports/TableReport.rb +47 -32
  426. data/lib/reports/TaskListRE.rb +3 -3
  427. data/lib/reports/TimeSheetReport.rb +14 -5
  428. data/lib/reports/TjpExportRE.rb +14 -19
  429. data/lib/taskjuggler3.rb +15 -0
  430. data/lib/tj3client.rb +9 -7
  431. data/lib/tj3d.rb +38 -10
  432. data/lib/tj3ss_sender.rb +7 -0
  433. data/lib/tj3ts_receiver.rb +1 -0
  434. data/manual/Day_To_Day_Juggling +1 -1
  435. data/manual/Rich_Text_Attributes +8 -0
  436. data/manual/TaskJuggler_2x_Migration +7 -0
  437. data/manual/The_TaskJuggler_Syntax +20 -6
  438. data/prj_cfg.rb +3 -2
  439. data/tasks/changelog.rake +36 -0
  440. data/tasks/csts.rake +2 -3
  441. data/tasks/gem.rake +10 -0
  442. data/tasks/missing.rake +0 -17
  443. data/test/TestSuite/ReportGenerator/Correct/Journal.html +63 -0
  444. data/test/TestSuite/ReportGenerator/Correct/Journal.tjp +14 -0
  445. data/test/TestSuite/{HTML-Reports/LogicalFunctions.tjp → ReportGenerator/Correct/LogicalFunctions1.tjp} +2 -2
  446. data/test/TestSuite/ReportGenerator/Correct/LogicalFunctions2.csv +3 -0
  447. data/test/TestSuite/ReportGenerator/Correct/LogicalFunctions2.tjp +19 -0
  448. data/test/TestSuite/ReportGenerator/Correct/css/tjmanual.css +66 -0
  449. data/test/TestSuite/ReportGenerator/Correct/css/tjreport.css +407 -0
  450. data/test/TestSuite/ReportGenerator/Correct/icons/details.png +0 -0
  451. data/test/TestSuite/ReportGenerator/Correct/icons/flag-green.png +0 -0
  452. data/test/TestSuite/ReportGenerator/Correct/icons/flag-red.png +0 -0
  453. data/test/TestSuite/ReportGenerator/Correct/icons/flag-yellow.png +0 -0
  454. data/test/TestSuite/ReportGenerator/Correct/icons/resource.png +0 -0
  455. data/test/TestSuite/ReportGenerator/Correct/icons/resourcegroup.png +0 -0
  456. data/test/TestSuite/ReportGenerator/Correct/icons/task.png +0 -0
  457. data/test/TestSuite/ReportGenerator/Correct/icons/taskgroup.png +0 -0
  458. data/test/TestSuite/ReportGenerator/Correct/icons/trend-down.png +0 -0
  459. data/test/TestSuite/ReportGenerator/Correct/icons/trend-flat.png +0 -0
  460. data/test/TestSuite/ReportGenerator/Correct/icons/trend-up.png +0 -0
  461. data/test/TestSuite/ReportGenerator/Correct/opennodes.csv +2 -0
  462. data/test/TestSuite/ReportGenerator/Correct/opennodes.tjp +26 -0
  463. data/test/TestSuite/ReportGenerator/Correct/refs/Journal-1.csv +6 -0
  464. data/test/TestSuite/ReportGenerator/Correct/refs/LogicalFunctions1-1.csv +7 -0
  465. data/test/TestSuite/ReportGenerator/Correct/refs/LogicalFunctions2-1.csv +2 -0
  466. data/test/TestSuite/ReportGenerator/Correct/refs/opennodes-1.csv +2 -0
  467. data/test/TestSuite/ReportGenerator/Correct/scripts/wz_tooltip.js +1301 -0
  468. data/test/TestSuite/ReportGenerator/Errors/rtp_report_recursion.tjp +20 -0
  469. data/test/TestSuite/Scheduler/Correct/Container.html +349 -0
  470. data/test/TestSuite/Scheduler/Correct/Limits.tjp +11 -4
  471. data/test/TestSuite/Scheduler/Correct/Shift2.html +464 -150
  472. data/test/TestSuite/Scheduler/Correct/TimeSheet2.html +108 -0
  473. data/test/TestSuite/Scheduler/Correct/TimeSheet2.tjp +7 -0
  474. data/test/TestSuite/Scheduler/Correct/css/tjmanual.css +14 -0
  475. data/test/TestSuite/Scheduler/Correct/css/tjreport.css +233 -21
  476. data/test/TestSuite/Scheduler/Correct/scripts/wz_tooltip.js +20 -20
  477. data/test/TestSuite/StatusSheetTemplates/project.tji +35 -0
  478. data/test/TestSuite/StatusSheetTemplates/project.tjp +56 -0
  479. data/test/TestSuite/StatusSheets/run +3 -2
  480. data/test/TestSuite/Syntax/Correct/Include.tjp +1 -1
  481. data/test/TestSuite/Syntax/Correct/Macro-1.tjp +1 -1
  482. data/test/TestSuite/Syntax/Correct/Macro-2.tjp +6 -0
  483. data/test/TestSuite/Syntax/Correct/Macro-3.tjp +14 -0
  484. data/test/TestSuite/Syntax/Correct/ResourcePrefix.html +32 -0
  485. data/test/TestSuite/Syntax/Correct/css/tjmanual.css +66 -0
  486. data/test/TestSuite/Syntax/Correct/css/tjreport.css +407 -0
  487. data/test/TestSuite/Syntax/Correct/icons/details.png +0 -0
  488. data/test/TestSuite/Syntax/Correct/icons/flag-green.png +0 -0
  489. data/test/TestSuite/Syntax/Correct/icons/flag-red.png +0 -0
  490. data/test/TestSuite/Syntax/Correct/icons/flag-yellow.png +0 -0
  491. data/test/TestSuite/Syntax/Correct/icons/resource.png +0 -0
  492. data/test/TestSuite/Syntax/Correct/icons/resourcegroup.png +0 -0
  493. data/test/TestSuite/Syntax/Correct/icons/task.png +0 -0
  494. data/test/TestSuite/Syntax/Correct/icons/taskgroup.png +0 -0
  495. data/test/TestSuite/Syntax/Correct/icons/trend-down.png +0 -0
  496. data/test/TestSuite/Syntax/Correct/icons/trend-flat.png +0 -0
  497. data/test/TestSuite/Syntax/Correct/icons/trend-up.png +0 -0
  498. data/test/TestSuite/Syntax/Correct/include/dir1/file2.tji +3 -0
  499. data/test/TestSuite/Syntax/Correct/include/dir1/file5.tji +2 -0
  500. data/test/TestSuite/Syntax/Correct/include/dir3/all.tji +3 -0
  501. data/test/TestSuite/Syntax/Correct/include/dir3/file1.tji +2 -0
  502. data/test/TestSuite/Syntax/Correct/include/dir3/file2.tji +1 -0
  503. data/test/TestSuite/Syntax/Correct/scripts/wz_tooltip.js +1301 -0
  504. data/test/TestSuite/Syntax/Correct/tutorial.tjp +13 -13
  505. data/test/TestSuite/Syntax/Errors/empty.tjp +1 -1
  506. data/test/TestSuite/Syntax/Errors/include_recursion.tjp +1 -0
  507. data/test/TestSuite/Syntax/Errors/macro_stack_overflow.tjp +2 -1
  508. data/test/TestSuite/Syntax/Errors/{eof_in_istring1.tjp → no_token_match1.tjp} +2 -2
  509. data/test/TestSuite/Syntax/Errors/{eof_in_istring2.tjp → no_token_match2.tjp} +2 -2
  510. data/test/TestSuite/Syntax/Errors/{eof_in_istring3.tjp → no_token_match3.tjp} +1 -1
  511. data/test/TestSuite/Syntax/Errors/{eof_in_istring4.tjp → no_token_match4.tjp} +1 -1
  512. data/test/TestSuite/Syntax/Errors/{eof_in_istring5.tjp → no_token_match5.tjp} +1 -1
  513. data/test/TestSuite/Syntax/Errors/not_scheduled.tjp +13 -0
  514. data/test/TestSuite/Syntax/Errors/unsupported_token.tjp +1 -1
  515. data/test/TestSuite/TimeSheets/run +5 -5
  516. data/test/test_CSVFile.rb +75 -0
  517. data/test/test_Limits.rb +63 -5
  518. data/test/test_ProjectFileScanner.rb +163 -0
  519. data/test/test_ReportGenerator.rb +81 -0
  520. data/test/test_RichText.rb +21 -3
  521. data/test/test_Scheduler.rb +1 -1
  522. data/test/test_ShiftAssignments.rb +4 -4
  523. data/test/test_Syntax.rb +1 -1
  524. data/test/test_URLParameter.rb +30 -0
  525. metadata +126 -32
  526. data/test/TestSuite/Scheduler/Errors/not_scheduled.tjp +0 -8
  527. data/test/TestSuite/Syntax/Errors/bad_comment.tjp +0 -7
  528. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/boss_2002-03-01.tji +0 -36
  529. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/dev1_2002-03-01.tji +0 -48
  530. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/dev2_2002-03-01.tji +0 -67
  531. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/dev3_2002-03-01.tji +0 -67
  532. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/doc_2002-03-01.tji +0 -48
  533. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/resources.yml +0 -31
  534. data/test/TestSuite/TimeSheets/TimeSheetTemplates/2002-03-01/test_2002-03-01.tji +0 -36
  535. data/test/TestSuite/TimeSheets/TimeSheetTemplates/acceptable_intervals +0 -1
  536. data/test/TestSuite/TimeSheets/TimeSheets/2002-03-01/all.tji +0 -1
  537. data/test/TestSuite/TimeSheets/TimeSheets/2002-03-01/dev2_2002-03-01.tji +0 -54
  538. data/test/TestSuite/TimeSheets/TimeSheets/all.tji +0 -2
  539. data/test/TestSuite/TimeSheets/receiver.log +0 -102
  540. data/test/TestSuite/TimeSheets/sender.log +0 -794
  541. data/test/TestSuite/TimeSheets/summary.log +0 -884
  542. data/test/TestSuite/TimeSheets/timesheets.log +0 -45
  543. data/test/TestSuite/TimeSheets/tj3d.log +0 -292
  544. data/test/test_TextScanner.rb +0 -95
@@ -120,8 +120,10 @@ class TaskJuggler::TextParser
120
120
  # Return the pattern of this rule that matches the given +token+. If no
121
121
  # pattern matches, return nil.
122
122
  def matchingPatternIndex(token)
123
- @transitions.length.times do |i|
124
- return i if @transitions[i].has_key?(token)
123
+ i = 0
124
+ @transitions.each do |t|
125
+ return i if t.has_key?(token)
126
+ i += 1
125
127
  end
126
128
 
127
129
  nil
@@ -25,20 +25,22 @@ class TaskJuggler::TextParser
25
25
  # called at the end to store the collected data. _sourceFileInfo_ is a
26
26
  # SourceFileInfo reference that describes the TextScanner position when the
27
27
  # rule was entered.
28
- def initialize(rule, function, sourceFileInfo)
28
+ def initialize(function)
29
29
  # This Array stores the collected values.
30
30
  @val = []
31
+ # Array to store the source file references for the collected values.
32
+ @sourceFileInfo = []
33
+ # Counter used for StackElement::store()
31
34
  @position = 0
32
- @rule = rule
35
+ # The method that will process the collected values.
33
36
  @function = function
34
- @sourceFileInfo = sourceFileInfo
35
37
  end
36
38
 
37
39
  # Store a collected value and move the position to the next pattern.
38
40
  def store(val, sourceFileInfo = nil)
39
41
  @val[@position] = val
42
+ @sourceFileInfo[@position] = sourceFileInfo
40
43
  @position += 1
41
- @sourceFileInfo = sourceFileInfo if sourceFileInfo
42
44
  end
43
45
 
44
46
  end
data/lib/TextScanner.rb CHANGED
@@ -10,6 +10,9 @@
10
10
  # published by the Free Software Foundation.
11
11
  #
12
12
 
13
+ require 'stringio'
14
+ require 'strscan'
15
+
13
16
  require 'UTF8String'
14
17
  require 'TjTime'
15
18
  require 'TjException'
@@ -20,10 +23,27 @@ require 'Log'
20
23
 
21
24
  class TaskJuggler
22
25
 
23
- # The TextScanner class can scan text files and chop then into tokens to be
24
- # used by a parser. Files can be nested. A file can include an other file.
26
+ # The TextScanner class is an abstract text scanner with support for nested
27
+ # include files and text macros. The tokenizer will operate on rules that
28
+ # must be provided by a derived class. The scanner is modal. Each mode
29
+ # operates only with the subset of token patterns that are assigned to the
30
+ # current mode. The current line is tracked accurately and can be used for
31
+ # error reporting. The scanner can operate on Strings or Files.
25
32
  class TextScanner
26
33
 
34
+ class MacroStackEntry
35
+
36
+ attr_reader :macro, :args, :text, :endPos
37
+
38
+ def initialize(macro, args, text, endPos)
39
+ @macro = macro
40
+ @args = args
41
+ @text = text
42
+ @endPos = endPos
43
+ end
44
+
45
+ end
46
+
27
47
  # This class is used to handle the low-level input operations. It knows
28
48
  # whether it deals with a text buffer or a file and abstracts this to the
29
49
  # TextScanner. For each nested file the scanner puts an StreamHandle on the
@@ -32,21 +52,102 @@ class TaskJuggler
32
52
  # completely processed.
33
53
  class StreamHandle
34
54
 
35
- attr_accessor :lineNo, :columnNo, :line, :charBuffer
36
- attr_reader :fileName
55
+ attr_reader :fileName, :macroStack
37
56
 
38
57
  def initialize
39
- @lineNo = 1
40
- @columnNo = 1
41
- @line = ""
42
- @charBuffer = []
43
58
  @fileName = nil
59
+ @stream = nil
60
+ @line = nil
61
+ @pos = 0
62
+ @endPos = 1
63
+ @scanner = nil
64
+ @wrapped = false
65
+ @macroStack = []
66
+ @nextMacroEnd = nil
67
+ end
68
+
69
+ def close
70
+ @stream = nil
71
+ end
72
+
73
+ def injectMacro(macro, args, text)
74
+ @nextMacroEnd = @pos + text.length
75
+ @line = @line[0, @pos] + text + @line[@pos..-1]
76
+ @scanner = StringScanner.new(@line)
77
+ @scanner.pos = @pos
78
+
79
+ # Simple detection for recursive macro calls.
80
+ return false if @macroStack.length > 20
81
+
82
+ @macroStack << MacroStackEntry.new(macro, args, text, @nextMacroEnd)
83
+ true
84
+ end
85
+
86
+ def scan(re)
87
+ # We read the file line by line with gets(). If we don't have a line
88
+ # yet or we've reached the end of a line, we get the next one.
89
+ if @scanner.nil? || @scanner.eos?
90
+ if (@line = @stream.gets)
91
+ # Update activity meter about every 1024 lines.
92
+ Log.activity if (@stream.lineno & 0x3FF) == 0
93
+ # Check for DOS or Mac end of line signatures.
94
+ if @line[-1] == ?\r
95
+ # Mac: Convert CR into LF
96
+ @line[-1] = ?\n
97
+ elsif @line[-2] == ?\r
98
+ # DOS: Convert CR+LF into LF
99
+ @line = @line.chomp + "\n"
100
+ end
101
+ else
102
+ # We've reached the end of the current file.
103
+ @scanner = nil
104
+ # Return special EOF symbol.
105
+ return :scannerEOF
106
+ end
107
+ @scanner = StringScanner.new(@line)
108
+ @wrapped = @line[-1] == ?\n
109
+ end
110
+ return nil if (token = @scanner.scan(re)).nil?
111
+ #puts "#{re.to_s[0..20]}: [#{token}]"
112
+
113
+ @pos = @scanner.pos
114
+ while @nextMacroEnd && @nextMacroEnd < @pos
115
+ @macroStack.pop
116
+ @nextMacroEnd = @macroStack.empty? ? nil : @macroStack.last.endPos
117
+ end
118
+
119
+ token
120
+ end
121
+
122
+ def peek(n)
123
+ @scanner ? @scanner.peek(n) : nil
124
+ end
125
+
126
+ def eof?
127
+ @stream.eof? && @scanner.eos?
44
128
  end
45
129
 
46
130
  def dirname
47
131
  @fileName ? File.dirname(@fileName) : ''
48
132
  end
49
133
 
134
+ # Return the number of the currently processed line.
135
+ def lineNo
136
+ # The IO object counts the lines for us by counting the gets() calls.
137
+ currentLine = @stream && @scanner ? @stream.lineno : 1
138
+ # If we've just read the LF, we have to add 1. The LF can only be the
139
+ # last character of the line.
140
+ currentLine += 1 if @wrapped && @line && @scanner && @scanner.eos?
141
+ currentLine
142
+ end
143
+
144
+ # Return the already processed part of the current line.
145
+ def line
146
+ return '' unless @line
147
+
148
+ @line[0..(@scanner.pos - 1)]
149
+ end
150
+
50
151
  end
51
152
 
52
153
  # Specialized version of StreamHandle for operations on files.
@@ -57,53 +158,14 @@ class TaskJuggler
57
158
  def initialize(fileName)
58
159
  super()
59
160
  @fileName = fileName.dup.untaint
60
- @file = fileName == '.' ? $stdin : File.new(@fileName, 'r')
61
- @bytes = 0
161
+ @stream = fileName == '.' ? $stdin : File.new(@fileName, 'r')
62
162
  Log << "Parsing file #{@fileName} ..."
63
163
  Log.startProgressMeter("Reading file #{fileName}")
64
164
  end
65
165
 
66
166
  def close
67
- @file.close unless @file == $stdin
68
- end
69
-
70
- def getc19
71
- Log.activity if @bytes & 0x3FFF == 0
72
-
73
- begin
74
- # This function converts CR+LF or CR into LF on the fly.
75
- if (c1 = @file.getc) == ?\r
76
- @bytes += 1
77
- # CR or CR/LF linebreaks
78
- if (c2 = @file.getc) == ?\n
79
- @bytes += 1
80
- # Ok, CR, LF
81
- return c2
82
- else
83
- # Just CR
84
- @file.ungetc(c2)
85
- return ?\n
86
- end
87
- else
88
- # This is for LF linebreaks and all other characters
89
- @bytes += 1
90
- return c1
91
- end
92
- rescue
93
- return nil
94
- end
95
- end
96
-
97
- def getc18
98
- c = getc19
99
- return nil if c.nil?
100
- '' << c
101
- end
102
-
103
- if RUBY_VERSION < '1.9.0'
104
- alias getc getc18
105
- else
106
- alias getc getc19
167
+ @stream.close unless @stream == $stdin
168
+ super
107
169
  end
108
170
 
109
171
  end
@@ -113,43 +175,10 @@ class TaskJuggler
113
175
 
114
176
  def initialize(buffer)
115
177
  super()
116
- @buffer = buffer
117
- @length = @buffer.length_utf8
118
- @pos = 0
119
- Log << "Parsing buffer #{@buffer[0, 20]} ..."
120
- end
121
-
122
- def close
123
- @buffer = nil
124
- end
125
-
126
- def getc18
127
- return nil if @pos >= @length
128
- '' << getc19
129
- end
130
-
131
- def getc19
132
- return nil if @pos >= @length
133
-
134
- # This function converts CR+LF or CR into LF on the fly.
135
- if (c = @buffer[@pos]) == "\r"
136
- # CR or CR+LF
137
- @pos += 1 if @buffer[@pos + 1] == "\n"
138
- c = "\n"
139
- end
140
- @pos += 1
141
- c
178
+ @stream = StringIO.new(buffer)
179
+ Log << "Parsing buffer #{buffer[0, 20]} ..."
142
180
  end
143
181
 
144
- if RUBY_VERSION < '1.9.0'
145
- alias getc getc18
146
- else
147
- alias getc getc19
148
- end
149
-
150
- def fileName
151
- ''
152
- end
153
182
  end
154
183
 
155
184
  # Create a new instance of TextScanner. _masterFile_ must be a String that
@@ -161,33 +190,78 @@ class TaskJuggler
161
190
  # This table contains all macros that may be expanded when found in the
162
191
  # text.
163
192
  @macroTable = MacroTable.new(messageHandler)
193
+ # The currently processed IO object.
194
+ @cf = nil
164
195
  # This Array stores the currently processed nested files. It's an Array
165
- # of Arrays. The nested Array consists of 3 elements, the @cf,
166
- # @tokenBuffer and the @pos of the file.
196
+ # of Arrays. The nested Array consists of 2 elements, the IO object and
197
+ # the @tokenBuffer.
167
198
  @fileStack = []
168
- # This Array stores the currently processed nested macros.
169
- @macroStack = []
170
- # In certain situation we want to ignore Macro replacement and this flag
171
- # is set to true.
172
- @ignoreMacros = false
199
+ # This flag is set if we have reached the end of a file. Since we will
200
+ # only know when the next new token is requested that the file is really
201
+ # done now, we have to use this flag.
202
+ @finishLastFile = false
203
+ # True if the scanner operates on a buffer.
173
204
  @fileNameIsBuffer = false
205
+ # A SourceFileInfo of the start of the currently processed token.
206
+ @startOfToken = nil
207
+ # Line number correction for error messages.
208
+ @lineDelta = 0
209
+ # Lists of regexps that describe the detectable tokens. The Arrays are
210
+ # grouped by mode.
211
+ @patternsByMode = { }
212
+ # The currently active scanner mode.
213
+ @scannerMode = nil
214
+ # Points to the currently active pattern set as defined by the mode.
215
+ @activePatterns = nil
216
+ end
217
+
218
+ # Add a new pattern to the scanner. _type_ is either nil for tokens that
219
+ # will be ignored, or some identifier that will be returned with each
220
+ # token of this type. _regExp_ is the RegExp that describes the token.
221
+ # _mode_ identifies the scanner mode where the pattern is active. If it's
222
+ # only a single mode, _mode_ specifies the mode directly. For multiple
223
+ # modes, it's an Array of modes. _postProc_ is a method reference. This
224
+ # method is called after the token has been detected. The method gets the
225
+ # type and the matching String and returns them again in an Array.
226
+ def addPattern(type, regExp, mode, postProc = nil)
227
+ if mode.is_a?(Array)
228
+ mode.each do |m|
229
+ # The pattern is active in multiple modes
230
+ @patternsByMode[m] = [] unless @patternsByMode.include?(m)
231
+ @patternsByMode[m] << [ type, regExp, postProc ]
232
+ end
233
+ else
234
+ # The pattern is only active in one specific mode.
235
+ @patternsByMode[mode] = [] unless @patternsByMode.include?(mode)
236
+ @patternsByMode[mode] << [ type, regExp, postProc ]
237
+ end
174
238
  end
175
239
 
240
+ # Switch the parser to another mode. The scanner will then only detect
241
+ # with pattens of that _newMode_.
242
+ def mode=(newMode)
243
+ #puts "**** New mode: #{newMode}"
244
+ @activePatterns = @patternsByMode[newMode]
245
+ raise "Undefined mode #{newMode}" unless @activePatterns
246
+ @scannerMode = newMode
247
+ end
248
+
249
+
176
250
  # Start the processing. if _fileNameIsBuffer_ is true, we operate on a
177
251
  # String, else on a File.
178
252
  def open(fileNameIsBuffer = false)
179
253
  @fileNameIsBuffer = fileNameIsBuffer
180
254
  if fileNameIsBuffer
181
- @fileStack = [ [ @cf = BufferStreamHandle.new(@masterFile), nil, nil ] ]
255
+ @fileStack = [ [ @cf = BufferStreamHandle.new(@masterFile), nil ] ]
182
256
  else
183
257
  begin
184
- @fileStack = [ [ @cf = FileStreamHandle.new(@masterFile), nil, nil ] ]
258
+ @fileStack = [ [ @cf = FileStreamHandle.new(@masterFile), nil ] ]
185
259
  rescue StandardError
186
260
  raise TjException.new, "Cannot open file #{@masterFile}"
187
261
  end
188
262
  end
189
263
  @masterPath = @cf.dirname + '/'
190
- @tokenBuffer = @pos = @lastPos = nil
264
+ @tokenBuffer = nil
191
265
  end
192
266
 
193
267
  # Finish processing and reset all data structures.
@@ -200,46 +274,46 @@ class TaskJuggler
200
274
  @cf = @tokenBuffer = nil
201
275
  end
202
276
 
203
- # Continue processing with a new file specified by _fileName_. When this
204
- # file is finished, we will continue in the old file after the location
205
- # where we started with the new file.
206
- def include(fileName)
207
- if fileName[0] != '/'
208
- if @fileStack.empty?
209
- path = @masterPath == './' ? '' : @masterPath
210
- else
211
- pathOfCallingFile = @fileStack.last[0].dirname
212
- path = pathOfCallingFile.empty? ? '' : pathOfCallingFile + '/'
213
- @fileStack.last[1, 2] = [ @tokenBuffer, @pos ]
214
- end
277
+ # Continue processing with a new file specified by _includeFileName_. When
278
+ # this file is finished, we will continue in the old file after the
279
+ # location where we started with the new file.
280
+ def include(includeFileName, sfi)
281
+ if includeFileName[0] != '/'
282
+ pathOfCallingFile = @fileStack.last[0].dirname
283
+ path = pathOfCallingFile.empty? ? '' : pathOfCallingFile + '/'
215
284
  # If the included file is not an absolute name, we interpret the file
216
285
  # name relative to the including file.
217
- fileName = path + fileName
286
+ includeFileName = path + includeFileName
218
287
  end
219
288
 
220
- @tokenBuffer = nil
221
-
222
- # Check the current file stack to find recusions.
223
- if @pos && fileName == @pos.fileName
224
- error('include_recursion', "Recursive inclusion of #{fileName} detected")
225
- else
226
- @fileStack.each do |entry|
227
- if fileName == entry[0].fileName
228
- error('include_recursion', "Recursive inclusion of #{fileName} " +
229
- "detected")
230
- end
289
+ # Try to dectect recursive inclusions. This will not work if files are
290
+ # accessed via filesystem links.
291
+ @fileStack.each do |entry|
292
+ if includeFileName == entry[0].fileName
293
+ error('include_recursion',
294
+ "Recursive inclusion of #{includeFileName} detected", nil, sfi)
231
295
  end
232
296
  end
297
+
298
+ # Save @tokenBuffer in the record of the parent file.
299
+ @fileStack.last[1] = @tokenBuffer unless @fileStack.empty?
300
+ @tokenBuffer = nil
301
+ @finishLastFile = false
302
+
303
+ # Open the new file and push the handle on the @fileStack.
233
304
  begin
234
- @fileStack << [ (@cf = FileStreamHandle.new(fileName)), nil, nil ]
305
+ @fileStack << [ (@cf = FileStreamHandle.new(includeFileName)), nil, ]
306
+ Log << "Parsing file #{includeFileName}"
235
307
  rescue StandardError
236
- error('bad_include', "Cannot open include file #{fileName}")
308
+ error('bad_include', "Cannot open include file #{includeFileName}",
309
+ nil, sfi)
237
310
  end
238
311
  end
239
312
 
240
313
  # Return SourceFileInfo for the current processing prosition.
241
314
  def sourceFileInfo
242
- @pos ? @pos.dup : SourceFileInfo.new(fileName, lineNo, columnNo)
315
+ @cf ? SourceFileInfo.new(fileName, @cf.lineNo - @lineDelta, 0) :
316
+ SourceFileInfo.new(@masterFile, 0, 0)
243
317
  end
244
318
 
245
319
  # Return the name of the currently processed file. If we are working on a
@@ -253,94 +327,97 @@ class TaskJuggler
253
327
  end
254
328
 
255
329
  def columnNo # :nodoc:
256
- @cf ? @cf.columnNo : 0
330
+ 0
257
331
  end
258
332
 
259
333
  def line # :nodoc:
260
334
  @cf ? @cf.line : 0
261
335
  end
262
336
 
263
- # Scan for the next token in the input stream and return it. The result will
264
- # be an Array of the form [ TokenType, TokenValue ].
337
+ # Return the next token from the input stream. The result is an Array with
338
+ # 3 entries: the token type, the token String and the SourceFileInfo where
339
+ # the token started.
265
340
  def nextToken
266
341
  # If we have a pushed-back token, return that first.
267
342
  unless @tokenBuffer.nil?
268
343
  res = @tokenBuffer
269
344
  @tokenBuffer = nil
270
- @pos = @tokenBufferPos
271
345
  return res
272
346
  end
273
347
 
348
+ if @finishLastFile
349
+ # The previously processed file has now really been processed to
350
+ # completion. Close it and remove the corresponding entry from the
351
+ # @fileStack.
352
+ @finishLastFile = false
353
+ #Log << "Completed file #{@cf.fileName}"
354
+ @cf.close
355
+ @fileStack.pop
356
+
357
+ if @fileStack.empty?
358
+ # We are done with the top-level file now.
359
+ @cf = @tokenBuffer = nil
360
+ @finishLastFile = true
361
+ return [ '.', '<END>', @startOfToken ]
362
+ else
363
+ # Continue parsing the file that included the current file.
364
+ @cf, tokenBuffer = @fileStack.last
365
+ Log << "Parsing file #{@cf.fileName} ..."
366
+ # If we have a left over token from previously processing this file,
367
+ # return it now.
368
+ return tokenBuffer if tokenBuffer
369
+ end
370
+ end
371
+
274
372
  # Start processing characters from the input.
275
- @startOfToken = SourceFileInfo.new(fileName, lineNo, columnNo)
276
- token = [ '.', '<END>' ]
277
- while c = nextChar
278
- case c
279
- when ' ', "\n", "\t"
280
- if (tok = readBlanks(c))
281
- token = tok
282
- break
373
+ @startOfToken = sourceFileInfo
374
+ loop do
375
+ match = nil
376
+ begin
377
+ @activePatterns.each do |type, re, postProc|
378
+ if (match = @cf.scan(re))
379
+ if match == :scannerEOF
380
+ # We've found the end of an input file. Return a special token
381
+ # that describes the end of a file.
382
+ @finishLastFile = true
383
+ return [ '.', '<END>', @startOfToken ]
384
+ end
385
+
386
+ raise "#{re} matches empty string" if match.empty?
387
+ # If we have a post processing method, call it now. It may modify
388
+ # the type or the found token String.
389
+ type, match = postProc.call(type, match) if postProc
390
+
391
+ break if type.nil? # Ignore certain tokens with nil type.
392
+
393
+ return [ type, match, @startOfToken ]
394
+ end
283
395
  end
284
- @startOfToken = SourceFileInfo.new(fileName, lineNo, columnNo)
285
- when '#'
286
- skipComment
287
- @startOfToken = SourceFileInfo.new(fileName, lineNo, columnNo)
288
- when '/'
289
- skipCPlusPlusComments
290
- @startOfToken = SourceFileInfo.new(fileName, lineNo, columnNo)
291
- when '0'..'9'
292
- token = readNumber(c)
293
- break
294
- when "'"
295
- token = readString(c)
296
- break
297
- when '"'
298
- token = readString(c)
299
- break
300
- when '-'
301
- token = handleDash
302
- break
303
- when '!'
304
- if (c = nextChar) == '='
305
- token = [ 'LITERAL', '!=' ]
396
+ rescue
397
+ error('scan_encoding_error', $!.to_s)
398
+ end
399
+
400
+ if match.nil?
401
+ if @cf.eof?
402
+ error('unexpected_eof',
403
+ "Unexpected end of file found")
306
404
  else
307
- returnChar(c)
308
- token = [ 'LITERAL', '!' ]
405
+ error('no_token_match',
406
+ "Unexpected characters found: '#{@cf.peek(10)}...'")
309
407
  end
310
- break
311
- when 'a'..'z', 'A'..'Z', '_'
312
- token = readId(c)
313
- break
314
- when '<', '>', '='
315
- token = readOperator(c)
316
- break
317
- when '['
318
- token = readMacro
319
- break
320
- when nil
321
- # We've reached an end of file or buffer
322
- break
323
- else
324
- str = ""
325
- str << c
326
- token = [ 'LITERAL', str ]
327
- break
328
408
  end
329
409
  end
330
- @pos = @startOfToken
331
- return token
332
410
  end
333
411
 
334
412
  # Return a token to retrieve it with the next nextToken() call again. Only 1
335
413
  # token can be returned before the next nextToken() call.
336
414
  def returnToken(token)
415
+ #Log << "-> Returning Token: [#{token[0]}][#{token[1]}]"
337
416
  unless @tokenBuffer.nil?
338
417
  $stderr.puts @tokenBuffer
339
418
  raise "Fatal Error: Cannot return more than 1 token in a row"
340
419
  end
341
420
  @tokenBuffer = token
342
- @tokenBufferPos = @pos
343
- @pos = @lastPos
344
421
  end
345
422
 
346
423
  # Add a Macro to the macro translation table.
@@ -353,548 +430,54 @@ class TaskJuggler
353
430
  @macroTable.include?(name)
354
431
  end
355
432
 
356
- def expandMacro(args)
433
+ # Expand a macro and inject it into the input stream. _prefix_ is any
434
+ # string that was found right before the macro call. We have to inject it
435
+ # before the expanded macro. _args_ is an Array of Strings. The first is
436
+ # the macro name, the rest are the parameters.
437
+ def expandMacro(prefix, args)
438
+ # Get the expanded macro from the @macroTable.
357
439
  macro, text = @macroTable.resolve(args, sourceFileInfo)
440
+ # If the expanded macro is empty, we can ignore it.
358
441
  return if text == ''
359
442
 
360
- if @macroStack.length > 20
443
+ unless @cf.injectMacro(macro, args, prefix + text)
361
444
  error('macro_stack_overflow', "Too many nested macro calls.")
362
445
  end
363
- @macroStack << [ macro, args ]
364
- # Mark end of macro with a 0 element
365
- @cf.charBuffer << 0
366
- text.reverse.each_utf8_char do |c|
367
- @cf.charBuffer << c
368
- end
369
- @cf.line = ''
370
446
  end
371
447
 
372
448
  # Call this function to report any errors related to the parsed input.
373
- def error(id, text, property = nil)
374
- message('error', id, text, property)
449
+ def error(id, text, property = nil, sfi = nil)
450
+ message('error', id, text, property, sfi)
375
451
  end
376
452
 
377
- def warning(id, text, property = nil)
378
- message('warning', id, text, property)
453
+ def warning(id, text, property = nil, sfi = nil)
454
+ message('warning', id, text, property, sfi)
379
455
  end
380
456
 
381
- def message(type, id, text, property)
457
+ def message(type, id, text, property, sfi)
382
458
  unless text.empty?
383
459
  message = Message.new(id, type, text + "\n" + line.to_s,
384
- property, nil, sourceFileInfo)
460
+ property, nil, sfi || sourceFileInfo)
385
461
  @messageHandler.send(message)
386
462
 
387
- until @macroStack.empty?
388
- macro, args = @macroStack.pop
389
- args.collect! { |a| '"' + a + '"' }
390
- message = Message.new('macro_stack', 'info',
391
- " #{macro.name} #{args.join(' ')}", nil, nil,
392
- macro.sourceFileInfo)
463
+ if @cf && !@cf.macroStack.empty?
464
+ message = Message.new('macro_stack', 'info', 'Macro call history:')
393
465
  @messageHandler.send(message)
394
- end
395
- end
396
-
397
- # An empty strings signals an already reported error
398
- raise TjException.new, '' if type == 'error'
399
- end
400
466
 
401
- private
402
-
403
- # This function is called by the scanner to get the next character. It
404
- # features a FIFO buffer that can hold any amount of returned characters.
405
- # When it has reached the end of the master file it returns nil.
406
- def nextChar
407
- # We've started to find the next token. @pos no longer marks the
408
- # position of the current token. Since we also store the EOF position in
409
- # @pos, don't reset it if we have processed all files completely.
410
- if @pos && @cf
411
- @lastPos = @pos
412
- @pos = nil
413
- end
414
-
415
- if (c = nextCharI) == '$' && !@ignoreMacros
416
- # Double $ are reduced to a single $.
417
- return c if (c = nextCharI) == '$'
418
-
419
- # Macros start with $( or ${. All other $. are ignored.
420
- if c != '(' && c != '{'
421
- returnChar(c)
422
- return '$'
423
- end
424
-
425
- @ignoreMacros = true
426
- returnChar(c)
427
- macroParser = MacroParser.new(self, @messageHandler)
428
- begin
429
- macroParser.parse('macroCall')
430
- rescue TjException
431
- end
432
- @ignoreMacros = false
433
- return nextCharI
434
- else
435
- return c
436
- end
437
- end
438
-
439
- def nextCharI
440
- # This can only happen when a previous call already returned nil.
441
- return nil if @cf.nil?
442
-
443
- c = nil
444
- # If there are characters in the return buffer process them first.
445
- # Otherwise get next character from input stream.
446
- unless @cf.charBuffer.empty?
447
- c = @cf.charBuffer.pop
448
- @cf.lineNo -= 1 if c == "\n" && !@macroStack.empty?
449
- while !@cf.charBuffer.empty? && @cf.charBuffer[-1] == 0
450
- @cf.charBuffer.pop
451
- @macroStack.pop
452
- end
453
- else
454
- # If EOF has been reached, try the parent file until even the master
455
- # file has been processed completely.
456
- if (c = @cf.getc).nil?
457
- # Safe current position so an EOF related error can be properly
458
- # reported.
459
- @pos = sourceFileInfo
460
-
461
- @cf.close
462
- @fileStack.pop
463
- if @fileStack.empty?
464
- # We are done with the top-level file now.
465
- @cf = @tokenBuffer = nil
466
- else
467
- @cf, @tokenBuffer, @lastPos = @fileStack.last
468
- Log << "Parsing file #{@cf.fileName} ..."
469
- # We have been called by nextToken() already, so we can't just
470
- # restore @tokenBuffer and be done. We need to feed the token text
471
- # back into the charBuffer and return the first character.
472
- if @tokenBuffer
473
- @tokenBuffer[1].reverse.each_utf8_char do |ch|
474
- @cf.charBuffer.push(ch)
475
- end
476
- @tokenBuffer = nil
477
- end
467
+ @cf.macroStack.reverse_each do |entry|
468
+ macro = entry.macro
469
+ args = entry.args[1..-1]
470
+ args.collect! { |a| '"' + a + '"' }
471
+ message = Message.new('macro_stack', 'info',
472
+ " ${#{macro.name} #{args.join(' ')}}",
473
+ nil, nil, macro.sourceFileInfo)
474
+ @messageHandler.send(message)
478
475
  end
479
- return nil
480
476
  end
481
477
  end
482
- unless c.nil?
483
- @cf.lineNo += 1 if c == "\n"
484
- @cf.line = "" if @cf.line[-1] == ?\n
485
- @cf.line << c
486
- end
487
- c
488
- end
489
-
490
- def returnChar(c)
491
- return if @cf.nil?
492
478
 
493
- @cf.line.chop! if c
494
- @cf.charBuffer << c
495
- @cf.lineNo -= 1 if c == "\n" && @macroStack.empty?
496
- end
497
-
498
- def skipComment
499
- # Read all characters until line or file end is found
500
- @ignoreMacros = true
501
- while (c = nextChar) && c != "\n"
502
- end
503
- @ignoreMacros = false
504
- returnChar(c)
505
- end
506
-
507
- def skipCPlusPlusComments
508
- if (c = nextChar) == '*'
509
- # /* */ style multi-line comment
510
- @ignoreMacros = true
511
- begin
512
- while (c = nextChar) != '*'
513
- end
514
- end until (c = nextChar) == '/'
515
- @ignoreMacros = false
516
- elsif c == '/'
517
- # // style single line comment
518
- skipComment
519
- else
520
- error('bad_comment', "'/' or '*' expected after start of comment")
521
- end
522
- end
523
-
524
- def handleDash
525
- if (c1 = nextChar) == '8'
526
- if (c2 = nextChar) == '<'
527
- if (c3 = nextChar) == '-'
528
- return readIndentedString
529
- else
530
- returnChar(c3)
531
- returnChar(c2)
532
- returnChar(c1)
533
- end
534
- else
535
- returnChar(c2)
536
- returnChar(c1)
537
- end
538
- else
539
- returnChar(c1)
540
- end
541
- return [ 'LITERAL', ' - ']
542
- end
543
-
544
- def readBlanks(c)
545
- loop do
546
- if c == ' '
547
- if (c2 = nextChar) == '-'
548
- # Special case for the dash between period dates. It must be
549
- # surrounded by blanks.
550
- if (c3 = nextChar) == ' '
551
- return [ 'LITERAL', ' - ']
552
- end
553
- returnChar(c3)
554
- end
555
- returnChar(c2)
556
- elsif c != "\n" && c != "\t"
557
- returnChar(c)
558
- return nil
559
- end
560
- c = nextChar
561
- end
562
- end
563
-
564
- def readNumber(c)
565
- token = ""
566
- token << c
567
- while ('0'..'9') === (c = nextChar)
568
- token << c
569
- end
570
- if c == '-'
571
- return readDate(token)
572
- elsif c == '.'
573
- frac = readDigits
574
-
575
- return [ 'FLOAT', token.to_f + frac.to_f / (10.0 ** frac.length) ]
576
- elsif c == ':'
577
- hours = token.to_i
578
- mins = readDigits.to_i
579
- if hours < 0 || hours > 24
580
- raise TjException.new, "Hour must be between 0 and 23"
581
- end
582
- if mins < 0 || mins > 59
583
- raise TjException.new, "Minutes must be between 0 and 59"
584
- end
585
- if hours == 24 && mins != 0
586
- raise TjException.new, "Time may not be larger than 24:00"
587
- end
588
-
589
- # Return time as seconds of day since midnight.
590
- return [ 'TIME', hours * 60 * 60 + mins * 60 ]
591
- else
592
- returnChar(c)
593
- end
594
-
595
- [ 'INTEGER', token.to_i ]
596
- end
597
-
598
- def readDate(token)
599
- year = token.to_i
600
- if year < 1970 || year > 2030
601
- raise TjException.new, "Year must be between 1970 and 2030"
602
- end
603
-
604
- month = readDigits.to_i
605
- if month < 1 || month > 12
606
- raise TjException.new, "Month must be between 1 and 12"
607
- end
608
- if nextChar != '-'
609
- raise TjException.new, "Corrupted date"
610
- end
611
-
612
- day = readDigits.to_i
613
- if day < 1 || day > 31
614
- raise TjException.new, "Day must be between 1 and 31"
615
- end
616
-
617
- if (c = nextChar) != '-'
618
- returnChar(c)
619
- return [ 'DATE', TjTime.local(year, month, day) ]
620
- end
621
-
622
- hour = readDigits.to_i
623
- if hour < 0 || hour > 23
624
- raise TjException.new, "Hour must be between 0 and 23"
625
- end
626
-
627
- if nextChar != ':'
628
- raise TjException.new, "Corrupted time. ':' expected."
629
- end
630
-
631
- minutes = readDigits.to_i
632
- if minutes < 0 || minutes > 59
633
- raise TjException.new, "Minutes must be between 0 and 59"
634
- end
635
-
636
- if (c = nextChar) == ':'
637
- seconds = readDigits.to_i
638
- if seconds < 0 || seconds > 59
639
- raise TjException.new, "Seconds must be between 0 and 59"
640
- end
641
- else
642
- seconds = 0
643
- returnChar(c)
644
- end
645
-
646
- if (c = nextChar) != '-'
647
- returnChar(c)
648
- return [ 'DATE', TjTime.local(year, month, day, hour, minutes, seconds) ]
649
- end
650
-
651
- if (c = nextChar) == '-'
652
- delta = 1
653
- elsif c == '+'
654
- delta = -1
655
- else
656
- # An actual time zone name
657
- tz = readId(c)[1]
658
- oldTz = ENV['TZ']
659
- ENV['TZ'] = tz
660
- timeVal = TjTime.local(year, month, day, hour, minutes, seconds)
661
- ENV['TZ'] = oldTz
662
- if timeVal.to_a[9] != tz
663
- raise TjException.new, "Unknown time zone #{tz}"
664
- end
665
- return [ 'DATE', timeVal ]
666
- end
667
-
668
- utcDiff = readDigits
669
- utcHour = utcDiff[0, 2].to_i
670
- if utcHour < 0 || utcHour > 23
671
- raise TjException.new, "Hour must be between 0 and 23"
672
- end
673
- utcMin = utcDiff[2, 2].to_i
674
- if utcMin < 0 || utcMin > 59
675
- raise TjException.new, "Minutes must be between 0 and 59"
676
- end
677
-
678
- [ 'DATE', TjTime.gm(year, month, day, hour, minutes, seconds) +
679
- delta * ((utcHour * 3600) + utcMin * 60) ]
680
- end
681
-
682
- def readString(terminator)
683
- token = ""
684
- while (c = nextChar) && c != terminator
685
- if c == "\\"
686
- # Terminators can be used as regular characters when prefixed by a \.
687
- if (c = nextChar) && c != terminator
688
- # \ followed by non-terminator. Just add both.
689
- token << "\\"
690
- end
691
- end
692
- token << c
693
- end
694
-
695
- [ 'STRING', token ]
696
- end
697
-
698
- def readIndentedString
699
- state = 0
700
- indent = ''
701
- token = ''
702
- while true
703
- case state
704
- when 0 # Determining indent
705
- # Skip trailing spaces and tabs.
706
- while (c = nextChar) == ' ' || c == "\t" do
707
- # empty on purpose
708
- end
709
- if c != "\n"
710
- error('junk_after_cut',
711
- 'The cut mark -8<- must be immediately followed by a ' +
712
- 'line break.')
713
- end
714
- while (c = nextChar) == ' ' || c == "\t"
715
- indent << c
716
- end
717
- @startOfToken = SourceFileInfo.new(fileName, lineNo, columnNo)
718
- returnChar(c)
719
- state = 1
720
- when 1 # reading '-' or first content line character
721
- if (c = nextChar) == '-'
722
- state = 3
723
- elsif c.nil?
724
- errorEOF(1, token)
725
- elsif c == "\n"
726
- token << c
727
- state = 6
728
- else
729
- token << c
730
- state = 2
731
- end
732
- when 2 # reading content line
733
- # The '->8-' is only valid if no other content preceded it on this
734
- # line.
735
- onlyBlanks = true
736
- while (c = nextChar) != "\n" && !(c == '-' && onlyBlanks)
737
- onlyBlanks = false if c != ' ' && c != "\t"
738
- errorEOF(2, token) if c.nil?
739
- token << c
740
- end
741
- if c == '-'
742
- # we may have found the start of '->8-'
743
- state = 3
744
- else
745
- token << c
746
- state = 6
747
- end
748
- when 3 # reading '>' of '->8-'
749
- if (c = nextChar) == '>'
750
- state = 4
751
- else
752
- errorEOF(3, token) if c.nil?
753
- token << '-'
754
- token << c
755
- state = 2
756
- end
757
- when 4 # reading '8' of '->8-'
758
- if (c = nextChar) == '8'
759
- state = 5
760
- else
761
- errorEOF(4, token) if c.nil?
762
- token << c
763
- state = 2
764
- end
765
- when 5 # reading '-' of '->8-'
766
- if (c = nextChar) == '-'
767
- return [ 'STRING', token ]
768
- else
769
- errorEOF(5, token) if c.nil?
770
- token << c
771
- state = 2
772
- end
773
- when 6 # reading indentation
774
- state = 1
775
- indent.each_utf8_char do |ci|
776
- if ci != (c = nextChar)
777
- if c == '-'
778
- state = 3
779
- break
780
- elsif c == "\n"
781
- returnChar(c)
782
- break
783
- else
784
- warning('bad_indent',
785
- "Not all lines of string have same indentation. " +
786
- "The first line of the string determines the " +
787
- "indentation for all subsequent lines of the same " +
788
- "string.")
789
- token << c
790
- state = 2
791
- break
792
- end
793
- end
794
- end
795
- else
796
- raise "State machine error"
797
- end
798
- end
799
- end
800
-
801
- def readId(c)
802
- token = ""
803
- token << c
804
- while (c = nextChar) &&
805
- (('a'..'z') === c || ('A'..'Z') === c || ('0'..'9') === c ||
806
- c == '_')
807
- token << c
808
- end
809
- if c == ':'
810
- return [ 'ID_WITH_COLON', token ]
811
- elsif c == '.'
812
- token << c
813
- loop do
814
- token += readIdentifier
815
- break if (c = nextChar) != '.'
816
- token += '.'
817
- end
818
- returnChar c
819
-
820
- return [ 'ABSOLUTE_ID', token ]
821
- else
822
- returnChar c
823
- return [ 'ID', token ]
824
- end
825
- end
826
-
827
- def readMacro
828
- # We can deal with ']' inside of the macro as long as each ']' is
829
- # preceeded by a corresponding '['.
830
- token = ''
831
- bracketLevel = 1
832
- while bracketLevel > 0
833
- case (c = nextCharI)
834
- when nil
835
- error('unterminated_macro', "Unterminated macro #{token}")
836
- when '['
837
- bracketLevel += 1
838
- when ']'
839
- bracketLevel -= 1
840
- end
841
- token << c unless bracketLevel == 0
842
- end
843
- return [ 'MACRO', token ]
844
- end
845
-
846
- # Read operators of logical expressions.
847
- def readOperator(c)
848
- case c
849
- when '='
850
- return [ 'LITERAL', '=' ]
851
- when '>'
852
- return [ 'LITERAL', '>=' ] if (c = nextChar) == '='
853
- returnChar(c)
854
- return [ 'LITERAL', '>' ]
855
- when '<'
856
- return [ 'LITERAL', '<=' ] if (c = nextChar) == '='
857
- returnChar(c)
858
- return [ 'LITERAL', '<' ]
859
- else
860
- raise "Unsupported operator #{c}"
861
- end
862
- end
863
-
864
- # Read only decimal digits and return the result als Fixnum.
865
- def readDigits
866
- token = ""
867
- while ('0'..'9') === (c = nextChar)
868
- token << c
869
- end
870
- # Make sure that we have read at least one digit.
871
- if token == ""
872
- raise TjException.new, "Digit (0 - 9) expected"
873
- end
874
- # Push back the non-digit that terminated the digits.
875
- returnChar(c)
876
- token
877
- end
878
-
879
- def readIdentifier(noDigit = true)
880
- token = ""
881
- while (c = nextChar) &&
882
- (('a'..'z') === c || ('A'..'Z') === c ||
883
- (!noDigit && (('0'..'9') === c)) || c == '_')
884
- token << c
885
- noDigit = false
886
- end
887
- returnChar(c)
888
- if token == ""
889
- raise TjException.new, "Identifier expected"
890
- end
891
- token
892
- end
893
-
894
- def errorEOF(no, token)
895
- error("eof_in_istring#{no}",
896
- "Unexpected end of file in string '#{token[0,20]}...'" +
897
- "starting in line #{@startOfToken.lineNo}.")
479
+ # An empty strings signals an already reported error
480
+ raise TjException.new, '' if type == 'error'
898
481
  end
899
482
 
900
483
  end