FreeOberon.Mod 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. MODULE FreeOberon;
  2. (* Copyright 2017-2019 Arthur Yefimov
  3. This file is part of Free Oberon.
  4. Free Oberon is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. Free Oberon is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with Free Oberon. If not, see <http://www.gnu.org/licenses/>.
  14. *)
  15. IMPORT G := Graph, T := Terminal, Files,
  16. OV, Editor, Term, Config, Strings, Out;
  17. CONST
  18. needFullscreen = TRUE;
  19. version* = '1.0.2';
  20. (* Direction of Selection *)
  21. dirLeft = 0;
  22. dirRight = 1;
  23. dirUp = 2;
  24. dirDown = 3;
  25. (* States *)
  26. stateEditor = 0;
  27. stateTerminal = 1;
  28. (* Character Classes *)
  29. charOther = 0; (*!FIXME Remove these constants *)
  30. charAlpha = 1;
  31. charDigit = 2;
  32. charMinusPlus = 3;
  33. charQuote = 4;
  34. charOpenBracket = 5;
  35. (* Token Classes *)
  36. tokenOther = 0;
  37. tokenKeyword = 1;
  38. tokenNumber = 2;
  39. tokenString = 3;
  40. tokenComment = 4;
  41. TYPE
  42. StrList = POINTER TO StrListDesc;
  43. StrListDesc = RECORD
  44. s: ARRAY 64 OF CHAR;
  45. next: StrList
  46. END;
  47. VAR
  48. progBuf: ARRAY 16300 OF CHAR; (* For interacting with a launched program *)
  49. inputBuf: ARRAY 16300 OF CHAR; (* Saves entered characters before Enter is pressed *)
  50. inputBufLen: INTEGER;
  51. programFinished: BOOLEAN;
  52. tempWindowed: BOOLEAN; (* True if editor is in windowed mode while program is running *)
  53. needWindowed: BOOLEAN;
  54. blockToggle: BOOLEAN; (* If true, ALT+ENTER will not toggle fullscreen *)
  55. app: OV.App;
  56. PROCEDURE IntToStr*(n: INTEGER; VAR s: ARRAY OF CHAR); (* !TODO move out *)
  57. (* LEN(s) > 1 *)
  58. VAR i, j: INTEGER; tmp: CHAR; neg: BOOLEAN;
  59. BEGIN
  60. IF n = 0 THEN
  61. s[0] := '0'; i := 1
  62. ELSE i := 0; neg := n < 0;
  63. IF neg THEN n := -n END;
  64. WHILE (n > 0) & (i < LEN(s) - 1) DO
  65. s[i] := CHR(ORD('0') + n MOD 10);
  66. n := n DIV 10; INC(i)
  67. END;
  68. IF neg & (i < LEN(s) - 1) THEN s[i] := '-'; INC(i) END
  69. END;
  70. s[i] := 0X; j := 0; DEC(i);
  71. WHILE j < i DO
  72. tmp := s[j]; s[j] := s[i]; s[i] := tmp;
  73. INC(j); DEC(i)
  74. END
  75. END IntToStr;
  76. PROCEDURE CountLines(s: ARRAY OF CHAR; width: INTEGER): INTEGER;
  77. VAR i, x, lines: INTEGER;
  78. BEGIN
  79. i := 0; x:= 0; lines := 1;
  80. WHILE s[i] # 0X DO
  81. IF s[i] = 0AX THEN
  82. INC(lines); x := 0
  83. ELSIF s[i] # 0DX THEN
  84. IF x = width - 1 THEN INC(lines); x := 0
  85. ELSE INC(x)
  86. END
  87. END;
  88. INC(i)
  89. END;
  90. RETURN lines
  91. END CountLines;
  92. PROCEDURE ShowErrors(s: ARRAY OF CHAR);
  93. VAR lines, width, x0, x, y, i: INTEGER;
  94. BEGIN
  95. width := T.charsX - 2;
  96. lines := CountLines(s, width);
  97. IF lines > 10 THEN lines := 10 END;
  98. i := 0; x0 := 1; x := x0;
  99. y := T.charsY - 2 - lines;
  100. WHILE (s[i] # 0X) & (y < T.charsY - 2) DO
  101. IF s[i] = 0AX THEN
  102. WHILE x < x0 + width DO (* Till end of line *)
  103. T.PutChar(x, y, ' ', 0, 3); INC(x)
  104. END;
  105. x := x0; INC(y)
  106. ELSIF s[i] # 0DX THEN
  107. T.PutChar(x, y, s[i], 0, 3);
  108. IF x = x0 + width - 1 THEN INC(y); x := x0
  109. ELSE INC(x)
  110. END
  111. END;
  112. INC(i)
  113. END;
  114. IF x > x0 THEN
  115. WHILE x < x0 + width DO
  116. T.PutChar(x, y, ' ', 0, 3); INC(x)
  117. END
  118. END;
  119. IF T.Draw() THEN G.Flip; G.Pause END (*!FIXME*)
  120. END ShowErrors;
  121. PROCEDURE StringsFindNext* (pattern, stringToSearch: ARRAY OF CHAR; startPos: INTEGER;
  122. VAR patternFound: BOOLEAN; VAR posOfPattern: INTEGER); (* !TODO move out *)
  123. VAR patternPos: INTEGER;
  124. BEGIN
  125. IF (startPos < Strings.Length (stringToSearch)) THEN
  126. patternPos := 0;
  127. LOOP
  128. IF (pattern[patternPos] = 0X) THEN
  129. (* reached end of pattern *)
  130. patternFound := TRUE;
  131. posOfPattern := startPos - patternPos;
  132. EXIT
  133. ELSIF (stringToSearch[startPos] = 0X) THEN
  134. (* end of string (but not of pattern) *)
  135. patternFound := FALSE;
  136. EXIT
  137. ELSIF (stringToSearch[startPos] = pattern[patternPos]) THEN
  138. (* characters identic, compare next one *)
  139. INC (startPos);
  140. INC (patternPos)
  141. ELSE
  142. (* difference found: reset indices and restart *)
  143. DEC(startPos, patternPos - 1);
  144. patternPos := 0
  145. END
  146. END
  147. ELSE patternFound := FALSE
  148. END
  149. END StringsFindNext;
  150. PROCEDURE ParseErrors(VAR s: ARRAY OF CHAR);
  151. VAR i, j, pos, st, len, skip: INTEGER; found: BOOLEAN;
  152. BEGIN
  153. StringsFindNext(' pos ', s, 0, found, i);
  154. IF found THEN (* Read the position *)
  155. WHILE (s[i] # 0X) & ((s[i] < '0') OR (s[i] > '9')) DO INC(i) END;
  156. IF (s[i] >= '0') & (s[i] <= '9') THEN
  157. pos := 0;
  158. REPEAT pos := pos * 10 + ORD(s[i]) - ORD('0'); INC(i)
  159. UNTIL (s[i] < '0') OR (s[i] > '9');
  160. (* Skip spaces before 'err' *)
  161. WHILE s[i] = ' ' DO INC(i) END;
  162. IF s[i] = 'e' THEN (* Assume 'err' reached *)
  163. skip := 3; (* Skip 3 characters *)
  164. WHILE (skip > 0) & (s[i] # 0X) DO INC(i); DEC(skip) END;
  165. WHILE s[i] = ' ' DO INC(i) END; (* Skip spaces *)
  166. WHILE (s[i] >= '0') & (s[i] <= '9') DO INC(i) END; (* Skip numbers *)
  167. WHILE s[i] = ' ' DO INC(i) END; (* Skip spaces *)
  168. s[0] := ' '; j := 1;
  169. WHILE s[i] >= ' ' DO s[j] := s[i]; INC(i); INC(j) END;
  170. (* Remove trailing spaces *)
  171. WHILE (j > 0) & (s[j - 1] = ' ') DO DEC(j) END;
  172. s[j] := '.'; INC(j); s[j] := 0X;
  173. (* Capitalize first letter (0th is a space). *)
  174. IF (s[1] >= 'a') & (s[1] <= 'z') THEN s[1] := CAP(s[1]) END;
  175. app.windows(Editor.Editor).text.MoveToPos(pos);
  176. Editor.PrintText(app.windows(Editor.Editor));
  177. T.ResetCursorBlink (* !FIXME *)
  178. END
  179. END
  180. END
  181. END ParseErrors;
  182. PROCEDURE HandleMouseMotion;
  183. VAR x, y, newX, newY: INTEGER;
  184. BEGIN
  185. G.GetMousePos(x, y);
  186. newX := x DIV T.charW; newY := y DIV T.charH;
  187. IF (newX # T.mouseX) OR (newY # T.mouseY) THEN T.MouseXY(newX, newY) END
  188. END HandleMouseMotion;
  189. PROCEDURE PollProgram;
  190. VAR len, i: INTEGER;
  191. err: INTEGER;
  192. s, sN: ARRAY 64 OF CHAR;
  193. PROCEDURE WriteProgBuf;
  194. VAR ch: CHAR; i: INTEGER;
  195. BEGIN
  196. i := 0;
  197. WHILE i < len DO
  198. ch := progBuf[i];
  199. IF ch = 0D0X THEN
  200. INC(i); ch := progBuf[i];
  201. IF ch = 081X THEN ch := CHR(240) (* Big Yo *)
  202. ELSE ch := CHR(ORD(ch) - 16)
  203. END
  204. ELSIF ch = 0D1X THEN
  205. INC(i); ch := CHR(ORD(progBuf[i]) + 96)
  206. ELSIF ch >= 080X THEN ch := '?'
  207. END;
  208. T.Write(ch); INC(i)
  209. END
  210. END WriteProgBuf;
  211. PROCEDURE Read(tillEnd: BOOLEAN);
  212. VAR loopLimit: INTEGER;
  213. BEGIN
  214. loopLimit := 5;
  215. REPEAT
  216. Term.ReadFromProcess(progBuf, len, LEN(progBuf));
  217. IF len > 0 THEN
  218. IF inputBufLen > 0 THEN
  219. FOR i := 0 TO inputBufLen - 1 DO T.Backspace END;
  220. inputBufLen := 0
  221. END;
  222. WriteProgBuf
  223. END;
  224. DEC(loopLimit)
  225. UNTIL (len <= 0) OR (loopLimit <= 0) & ~tillEnd
  226. END Read;
  227. BEGIN
  228. IF ~programFinished THEN
  229. IF Term.ProcessFinished(err) THEN
  230. Read(TRUE); (* Read everything until pipe is empty *)
  231. programFinished := TRUE;
  232. IF tempWindowed THEN G.SwitchToFullscreen END;
  233. IF err = 0 THEN
  234. T.WriteString(' Press any key to return to IDE')
  235. ELSE
  236. s := ' Runtime error ';
  237. IntToStr(err, sN); Strings.Append(sN, s);
  238. T.WriteString(s)
  239. END
  240. ELSE
  241. Read(FALSE) (* Attempt several reads *)
  242. END
  243. END
  244. END PollProgram;
  245. PROCEDURE WriteToProcess(s: ARRAY OF CHAR; len: INTEGER);
  246. VAR buf: ARRAY 2048 OF CHAR; i, bufLen: INTEGER; ch: CHAR;
  247. BEGIN
  248. bufLen := 0; i := 0;
  249. WHILE i < len DO
  250. ch := s[i];
  251. IF ch < 80X THEN
  252. buf[bufLen] := ch; INC(bufLen)
  253. ELSIF ORD(ch) = 240 THEN (* Big cyrillic Yo *)
  254. buf[bufLen] := 0D0X; buf[bufLen + 1] := 81X;
  255. INC(bufLen, 2)
  256. ELSIF ORD(ch) < 224 THEN (* Before small cyrillic R *)
  257. buf[bufLen] := 0D0X;
  258. buf[bufLen + 1] := CHR(ORD(ch) - 128 + 090H);
  259. INC(bufLen, 2)
  260. ELSE
  261. buf[bufLen] := 0D1X;
  262. buf[bufLen + 1] := CHR(ORD(ch) - 224 + 080H);
  263. INC(bufLen, 2)
  264. END;
  265. INC(i)
  266. END;
  267. Term.WriteToProcess(buf, bufLen)
  268. END WriteToProcess;
  269. PROCEDURE HandleTerminalKeyDown(key: G.Key; VAR quit: BOOLEAN);
  270. VAR code: INTEGER; ch: CHAR; buf: ARRAY 2 OF CHAR;
  271. BEGIN
  272. IF programFinished THEN
  273. IF (key.code = G.kEnter) & (key.mod * G.mAlt # {}) THEN
  274. T.ToggleFullscreen
  275. ELSIF (key.code # G.kAlt) & (key.code # G.kAltGr) THEN quit := TRUE
  276. END
  277. ELSE
  278. CASE key.code OF
  279. G.kEnter, G.kEnterPad:
  280. IF key.mod * G.mAlt # {} THEN
  281. IF blockToggle THEN blockToggle := FALSE
  282. ELSE T.ToggleFullscreen; blockToggle := TRUE
  283. END
  284. ELSE T.Ln; WriteToProcess(inputBuf, inputBufLen);
  285. inputBufLen := 0; buf[0] := 0AX;
  286. Term.WriteToProcess(buf, 1)
  287. END
  288. | G.kBackspace:
  289. IF (inputBufLen > 0) THEN
  290. DEC(inputBufLen); T.Backspace
  291. END
  292. | G.kPause:
  293. IF G.CtrlPressed() THEN
  294. programFinished := TRUE;
  295. quit := TRUE (* !FIXME Kill the process *)
  296. END
  297. ELSE
  298. END
  299. END
  300. END HandleTerminalKeyDown;
  301. PROCEDURE HandleTerminalTextInput(s: ARRAY OF CHAR; sym: INTEGER);
  302. BEGIN
  303. IF (sym # 0) & (inputBufLen < LEN(inputBuf)) THEN
  304. inputBuf[inputBufLen] := CHR(sym); INC(inputBufLen);
  305. T.Write(CHR(sym))
  306. END
  307. END HandleTerminalTextInput;
  308. PROCEDURE RunTerminal;
  309. VAR event: G.Event; quit: BOOLEAN;
  310. BEGIN quit := FALSE;
  311. T.ClearScreen; T.GoToXY(0, 0);
  312. REPEAT
  313. G.WaitEvents(50);
  314. WHILE G.PollEvent(event) DO
  315. CASE event.type OF
  316. G.mouseMove: HandleMouseMotion
  317. | G.keyDown: HandleTerminalKeyDown(event.key, quit)
  318. | G.textInput: HandleTerminalTextInput(event.s, event.key.sym)
  319. ELSE
  320. END
  321. END;
  322. PollProgram;
  323. T.Act;
  324. IF T.Draw() THEN G.Flip ELSE G.RepeatFlip END
  325. UNTIL quit
  326. END RunTerminal;
  327. PROCEDURE Compile(filename: ARRAY OF CHAR; graph: BOOLEAN): BOOLEAN;
  328. CONST bufLen = 20480;
  329. VAR buf: ARRAY bufLen OF CHAR;
  330. len, err: INTEGER;
  331. scriptPostfix: ARRAY 32 OF CHAR;
  332. cmd: ARRAY 1024 OF CHAR;
  333. s, sN: ARRAY 80 OF CHAR;
  334. success: BOOLEAN;
  335. BEGIN
  336. IF ~graph THEN scriptPostfix := '_no_graph' ELSE scriptPostfix := '' END;
  337. IF Config.isWindows THEN
  338. IF Term.SearchPath('cmd.exe', cmd) # 0 THEN
  339. Strings.Insert('"', 0, cmd);
  340. Strings.Append('" /C data\bin\compile', cmd);
  341. Strings.Append(scriptPostfix, cmd);
  342. Strings.Append('.bat ', cmd)
  343. ELSE T.PutString(0, T.charsY - 1, 'Could not find cmd.exe', 15, 4, 0)
  344. END
  345. ELSE (* Linux *)
  346. COPY('data/bin/compile', cmd);
  347. Strings.Append(scriptPostfix, cmd);
  348. Strings.Append('.sh ', cmd)
  349. END;
  350. Strings.Append(filename, cmd);
  351. success := (Term.RunProcess(cmd, buf, bufLen, len, err) # 0) &
  352. (err = 0);
  353. IF ~success THEN
  354. COPY(' Compilation returned ', s);
  355. IntToStr(err, sN);
  356. Strings.Append(sN, s);
  357. Strings.Append(' exit status ', s);
  358. IF (len > 0) & (len < bufLen) THEN
  359. IF buf[len - 1] = 0AX THEN buf[len - 1] := 0X
  360. ELSE buf[len] := 0X
  361. END;
  362. ParseErrors(buf)
  363. ELSE COPY(' Compilation failed.', buf)
  364. END;
  365. IF buf[0] = 0X THEN ShowErrors(s)
  366. ELSE ShowErrors(buf)
  367. END
  368. END;
  369. RETURN success
  370. END Compile;
  371. PROCEDURE RunProgram(prg: ARRAY OF CHAR);
  372. VAR cmd: ARRAY 128 OF CHAR;
  373. x: INTEGER;
  374. BEGIN
  375. (* Extract 'Prg' from 'Prg.Mod' or 'dir/Prg.Mod' *)
  376. x := Strings.Length(prg);
  377. WHILE (x > 0) & (prg[x] # '.') DO DEC(x) END;
  378. IF prg[x] = '.' THEN prg[x] := 0X END;
  379. WHILE (x >= 0) & (prg[x] # '/') DO DEC(x) END;
  380. IF x >= 0 THEN Strings.Delete(prg, 0, x + 1) END;
  381. (* Construct 'bin/MyProg' or 'bin\MyProg' *)
  382. IF Config.isWindows THEN COPY('bin\', cmd) ELSE COPY('bin/', cmd) END;
  383. Strings.Append(prg, cmd);
  384. IF ~Term.StartProcess(cmd) THEN
  385. T.PutString(0, T.charsY - 1, ' Program execution failed ', 15, 4, 0);
  386. IF T.Draw() THEN G.Flip; G.Pause END
  387. ELSE
  388. programFinished := FALSE;
  389. RunTerminal
  390. END
  391. END RunProgram;
  392. PROCEDURE FileNew(c: OV.Control);
  393. VAR e: Editor.Editor;
  394. p, br: OV.Control;
  395. count: INTEGER;
  396. BEGIN e := Editor.NewEditor();
  397. p := app.windows; br := p; count := 0;
  398. WHILE p # NIL DO INC(count);
  399. IF p.next = br THEN p := NIL ELSE p := p.next END
  400. END;
  401. IF app.windows # NIL THEN
  402. e.x := app.windows.x + 1; e.y := app.windows.y + 1;
  403. e.w := app.windows.w - 1; e.h := app.windows.h - 1
  404. END;
  405. e.caption := 'NONAME??.Mod';
  406. e.caption[6] := CHR(ORD('0') + count DIV 10 MOD 10);
  407. e.caption[7] := CHR(ORD('0') + count MOD 10);
  408. OV.AddWindow(app, e)
  409. END FileNew;
  410. PROCEDURE DoOpenFile(c: OV.Control; filename: ARRAY OF CHAR);
  411. VAR e: Editor.Editor; newWin: BOOLEAN;
  412. BEGIN
  413. IF (c.app.windows # NIL) & (c.app.windows IS Editor.Editor) THEN
  414. e := c.app.windows(Editor.Editor)
  415. ELSE e := NIL
  416. END;
  417. newWin := (e = NIL) OR ~Editor.IsEmpty(e);
  418. IF newWin THEN e := Editor.NewEditor() END;
  419. IF e.text.LoadFromFile(filename) THEN
  420. e.caption := filename; e.filename := filename;
  421. IF newWin THEN OV.AddWindow(app, e) END
  422. END
  423. END DoOpenFile;
  424. PROCEDURE DoSaveFile(c: OV.Control; filename: ARRAY OF CHAR);
  425. VAR w: OV.Window; e: Editor.Editor;
  426. BEGIN
  427. IF filename[0] # 0X THEN w := c.app.windows;
  428. IF (w # NIL) & (w IS Editor.Editor) THEN e := w(Editor.Editor);
  429. IF e.text.SaveToFile(filename) THEN
  430. COPY(filename, e.caption); e.filename := filename
  431. END
  432. END
  433. END
  434. END DoSaveFile;
  435. PROCEDURE FileOpen(c: OV.Control);
  436. VAR w: Editor.FileDialog;
  437. BEGIN
  438. w := Editor.NewFileDialog(Editor.open);
  439. w.onFileOk := DoOpenFile;
  440. OV.AddWindow(app, w)
  441. END FileOpen;
  442. PROCEDURE FileReload(c: OV.Control);
  443. VAR e: Editor.Editor;
  444. BEGIN
  445. IF (c.app.windows # NIL) & (c.app.windows IS Editor.Editor) THEN
  446. e := c.app.windows(Editor.Editor);
  447. IF e.filename[0] # 0X THEN
  448. IF e.text.LoadFromFile(e.filename) THEN (*!FIXME*) END
  449. END
  450. END
  451. END FileReload;
  452. PROCEDURE FileSaveAs(c: OV.Control);
  453. VAR d: Editor.FileDialog;
  454. w: OV.Window; e: Editor.Editor;
  455. BEGIN d := Editor.NewFileDialog(Editor.save);
  456. d.onFileOk := DoSaveFile;
  457. w := c.app.windows;
  458. IF (w # NIL) & (w IS Editor.Editor) THEN e := w(Editor.Editor);
  459. IF e.filename[0] # 0X THEN
  460. OV.EditSetCaption(d.edtFilename, e.filename)
  461. END
  462. END;
  463. OV.AddWindow(app, d)
  464. END FileSaveAs;
  465. PROCEDURE FileSave(c: OV.Control);
  466. VAR w: OV.Window;
  467. BEGIN w := c.app.windows;
  468. IF (w # NIL) & (w IS Editor.Editor) THEN
  469. IF w(Editor.Editor).filename[0] = 0X THEN FileSaveAs(c)
  470. ELSE DoSaveFile(c, w(Editor.Editor).filename)
  471. END
  472. END
  473. END FileSave;
  474. PROCEDURE SkipComment(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
  475. VAR last: CHAR;
  476. BEGIN last := ch; Files.Read(R, ch);
  477. WHILE ~R.eof & ((last # '*') OR (ch # ')')) DO
  478. IF (last = '(') & (ch = '*') THEN SkipComment(R, ch, s) END;
  479. last := ch; Files.Read(R, ch)
  480. END;
  481. IF ~R.eof THEN Files.Read(R, ch) END;
  482. WHILE ~R.eof & (ch <= ' ') DO Files.Read(R, ch) END
  483. END SkipComment;
  484. PROCEDURE GetSym(VAR R: Files.Rider; VAR ch: CHAR; VAR s: ARRAY OF CHAR);
  485. VAR i: INTEGER;
  486. BEGIN
  487. WHILE ~R.eof & (ch <= ' ') DO Files.Read(R, ch) END;
  488. i := 0;
  489. IF ~R.eof THEN
  490. IF ch = '(' THEN
  491. Files.Read(R, ch);
  492. IF ch = '*' THEN Files.Read(R, ch); SkipComment(R, ch, s)
  493. ELSE s[i] := ch; INC(i)
  494. END
  495. END;
  496. IF ('A' <= CAP(ch)) & (CAP(ch) <= 'Z') THEN
  497. WHILE ~R.eof &
  498. (('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
  499. ('0' <= ch) & (ch <= '9')) DO
  500. IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
  501. Files.Read(R, ch)
  502. END
  503. ELSE
  504. WHILE ~R.eof & (ch > ' ') &
  505. ~(('A' <= CAP(ch)) & (CAP(ch) <= 'Z') OR
  506. ('0' <= ch) & (ch <= '9')) DO
  507. IF i < LEN(s) - 1 THEN s[i] := ch; INC(i) END;
  508. Files.Read(R, ch)
  509. END
  510. END
  511. END;
  512. s[i] := 0X
  513. END GetSym;
  514. PROCEDURE GetImportedModules(filename: ARRAY OF CHAR): StrList;
  515. VAR F: Files.File;
  516. R: Files.Rider;
  517. top, p: StrList;
  518. ch: CHAR;
  519. s: ARRAY 64 OF CHAR;
  520. ok: BOOLEAN;
  521. BEGIN NEW(top); top.next := NIL; p := top;
  522. s := 'Programs/'; Strings.Append(filename, s);
  523. F := Files.Old(s);
  524. IF F # NIL THEN
  525. Files.Set(R, F, 0); Files.Read(R, ch); GetSym(R, ch, s);
  526. ok := s = 'MODULE'; GetSym(R, ch, s); GetSym(R, ch, s);
  527. IF ok THEN
  528. ok := s = ';'; GetSym(R, ch, s);
  529. IF ok THEN
  530. ok := s = 'IMPORT'; GetSym(R, ch, s);
  531. WHILE ok & ('A' <= CAP(s[0])) & (CAP(s[0]) <= 'Z') DO
  532. NEW(p.next); p := p.next; p.next := NIL; p.s := s;
  533. GetSym(R, ch, s);
  534. IF s = ':=' THEN GetSym(R, ch, s); p.s := s; GetSym(R, ch, s) END;
  535. IF s = ',' THEN GetSym(R, ch, s) ELSE ok := FALSE END
  536. END
  537. END
  538. END
  539. END;
  540. RETURN top.next
  541. END GetImportedModules;
  542. PROCEDURE GetAllImportedModules(filename: ARRAY OF CHAR): StrList;
  543. VAR list: StrList;
  544. BEGIN
  545. list := GetImportedModules(filename);
  546. (*!TODO recursion*)
  547. RETURN list
  548. END GetAllImportedModules;
  549. PROCEDURE ImportsGraph(p: StrList): BOOLEAN;
  550. BEGIN
  551. WHILE (p # NIL) & (p.s # 'Graph') DO p := p.next END;
  552. RETURN p # NIL
  553. END ImportsGraph;
  554. PROCEDURE OnBuild(c: OV.Control);
  555. VAR w: OV.Window; graph: BOOLEAN;
  556. primaryFile: ARRAY 256 OF CHAR;
  557. p: StrList;
  558. BEGIN w := c.app.windows;
  559. IF (w # NIL) & (w IS Editor.Editor) THEN
  560. IF Editor.TextChanged(w(Editor.Editor)) THEN FileSave(c) END;
  561. IF w(Editor.Editor).filename[0] # 0X THEN
  562. COPY(w(Editor.Editor).filename, primaryFile);
  563. p := GetAllImportedModules(primaryFile);
  564. graph := ImportsGraph(p);
  565. needWindowed := graph;
  566. IF Compile(w(Editor.Editor).filename, graph) THEN
  567. tempWindowed := needWindowed & T.isFullscreen;
  568. IF tempWindowed THEN G.SwitchToWindowed END;
  569. RunProgram(w(Editor.Editor).filename)
  570. END
  571. END
  572. END
  573. END OnBuild;
  574. PROCEDURE HelpAbout(c: OV.Control);
  575. BEGIN
  576. IF app.statusText[0] # 0X THEN OV.SetStatusText(app, '')
  577. ELSE OV.SetStatusText(app, 'Visit freeoberon.su')
  578. END
  579. END HelpAbout;
  580. PROCEDURE InitIDE;
  581. VAR w: OV.Window;
  582. m, m2: OV.Menu;
  583. BEGIN
  584. app := OV.NewApp();
  585. FileNew(app.menu);
  586. m := OV.NewMenu('&File', '', 0, NIL);
  587. OV.Add(m, OV.NewMenu('&New', 'Shift+F3', OV.hShiftF3, FileNew));
  588. OV.Add(m, OV.NewMenu('&Open', 'F3', OV.hF3, FileOpen));
  589. OV.Add(m, OV.NewMenu('&Reload', '', 0, FileReload));
  590. OV.Add(m, OV.NewMenu('&Save', 'F2', OV.hF2, FileSave));
  591. OV.Add(m, OV.NewMenu('Save &as...', 'Shift+F2', OV.hShiftF2, FileSaveAs));
  592. (*OV.Add(m, OV.NewMenu('Save a&ll', '', 0, NIL));*)
  593. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  594. OV.Add(m, OV.NewMenu('E&xit', 'Alt+X', OV.hAltX, OV.QuitApp));
  595. OV.AddMenu(app, m);
  596. m := OV.NewMenu('&Edit', '', 0, NIL);
  597. m2 := OV.NewMenu('&Undo', 'DelText', OV.hAltBackspace, NIL); m2.status := OV.disabled;
  598. OV.Add(m, m2);
  599. m2 := OV.NewMenu('&Redo', '', 0, NIL); m2.status := OV.disabled;
  600. OV.Add(m, m2);
  601. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  602. OV.Add(m, OV.NewMenu('Cu&t', 'Ctrl+X', OV.hCtrlX, Editor.EditCut));
  603. OV.Add(m, OV.NewMenu('&Copy', 'Ctrl+C', OV.hCtrlC, Editor.EditCopy));
  604. OV.Add(m, OV.NewMenu('&Paste', 'Ctrl+V', OV.hCtrlV, Editor.EditPaste));
  605. OV.Add(m, OV.NewMenu('C&lear', 'Ctrl+Del', OV.hCtrlDel, Editor.EditClear));
  606. OV.Add(m, OV.NewMenu('Select &All', 'Ctrl+A', OV.hCtrlA, Editor.EditSelectAll));
  607. OV.Add(m, OV.NewMenu('U&nselect', '', 0, Editor.EditUnselect));
  608. OV.AddMenu(app, m);
  609. m := OV.NewMenu('&Search', '', 0, NIL);
  610. OV.Add(m, OV.NewMenu('&Find...', '', 0, NIL));
  611. (*!TODO*) m.children.prev.status := OV.disabled;
  612. OV.Add(m, OV.NewMenu('&Replace...', '', 0, NIL));
  613. (*!TODO*) m.children.prev.status := OV.disabled;
  614. OV.Add(m, OV.NewMenu('&Search again', '', 0, NIL));
  615. (*!TODO*) m.children.prev.status := OV.disabled;
  616. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  617. OV.Add(m, OV.NewMenu('&Go to line number...', '', 0, NIL));
  618. (*!TODO*) m.children.prev.status := OV.disabled;
  619. OV.Add(m, OV.NewMenu('&Find procedure...', '', 0, NIL));
  620. (*!TODO*) m.children.prev.status := OV.disabled;
  621. OV.AddMenu(app, m);
  622. m := OV.NewMenu('&Run', '', 0, NIL);
  623. OV.Add(m, OV.NewMenu('&Run', 'Ctrl+F9', OV.hCtrlF9, OnBuild));
  624. OV.Add(m, OV.NewMenu('Run &Directory...', '', 0, NIL));
  625. (*!TODO*) m.children.prev.status := OV.disabled;
  626. OV.Add(m, OV.NewMenu('P&arameters...', '', 0, NIL));
  627. (*!TODO*) m.children.prev.status := OV.disabled;
  628. OV.AddMenu(app, m);
  629. m := OV.NewMenu('&Compile', '', 0, NIL);
  630. OV.Add(m, OV.NewMenu('&Compile', 'Alt+F9', OV.hAltF9, OnBuild));
  631. OV.Add(m, OV.NewMenu('&Make', 'Shift+F9', OV.hShiftF9, OnBuild));
  632. OV.Add(m, OV.NewMenu('Make && &Run', 'F9', OV.hF9, OnBuild));
  633. OV.Add(m, OV.NewMenu('&Build', '', 0, OnBuild));
  634. OV.AddMenu(app, m);
  635. m := OV.NewMenu('&Debug', '', 0, NIL);
  636. OV.Add(m, OV.NewMenu('&Output', '', 0, NIL));
  637. (*!TODO*) m.children.prev.status := OV.disabled;
  638. OV.AddMenu(app, m);
  639. m := OV.NewMenu('&Tools', '', 0, NIL);
  640. OV.Add(m, OV.NewMenu('&Messages', 'F11', OV.hF11, NIL));
  641. (*!TODO*) m.children.prev.status := OV.disabled;
  642. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  643. OV.Add(m, OV.NewMenu('&Calculator', '', 0, NIL));
  644. (*!TODO*) m.children.prev.status := OV.disabled;
  645. OV.Add(m, OV.NewMenu('Ascii &table', '', 0, NIL));
  646. (*!TODO*) m.children.prev.status := OV.disabled;
  647. OV.AddMenu(app, m);
  648. m := OV.NewMenu('&Options', '', 0, NIL);
  649. OV.Add(m, OV.NewMenu('Mode&...', 'Normal', 0, NIL));
  650. (*!TODO*) m.children.prev.status := OV.disabled;
  651. OV.Add(m, OV.NewMenu('&Compiler...', '', 0, NIL));
  652. (*!TODO*) m.children.prev.status := OV.disabled;
  653. OV.Add(m, OV.NewMenu('&Memory sizes...', '', 0, NIL));
  654. (*!TODO*) m.children.prev.status := OV.disabled;
  655. OV.Add(m, OV.NewMenu('&Linker...', '', 0, NIL));
  656. (*!TODO*) m.children.prev.status := OV.disabled;
  657. OV.Add(m, OV.NewMenu('&Directories...', '', 0, NIL));
  658. (*!TODO*) m.children.prev.status := OV.disabled;
  659. OV.Add(m, OV.NewMenu('&Tools...', '', 0, NIL));
  660. (*!TODO*) m.children.prev.status := OV.disabled;
  661. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  662. m2 := OV.NewMenu('&Environment', '', 0, NIL);
  663. OV.Add(m2, OV.NewMenu('&Preferences...', '', 0, NIL));
  664. (*!TODO*) m2.children.prev.status := OV.disabled;
  665. OV.Add(m2, OV.NewMenu('&Editor...', '', 0, NIL));
  666. (*!TODO*) m2.children.prev.status := OV.disabled;
  667. OV.Add(m2, OV.NewMenu('Code&Complete...', '', 0, NIL));
  668. (*!TODO*) m2.children.prev.status := OV.disabled;
  669. OV.Add(m2, OV.NewMenu('Code&Templates...', '', 0, NIL));
  670. (*!TODO*) m2.children.prev.status := OV.disabled;
  671. OV.Add(m2, OV.NewMenu('&Desktop...', '', 0, NIL));
  672. (*!TODO*) m2.children.prev.status := OV.disabled;
  673. OV.Add(m2, OV.NewMenu('Keyboard && &mouse...', '', 0, NIL));
  674. (*!TODO*) m2.children.prev.status := OV.disabled;
  675. OV.Add(m2, OV.NewMenu('Learn &Keys', '', 0, NIL));
  676. (*!TODO*) m2.children.prev.status := OV.disabled;
  677. OV.Add(m, m2);
  678. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  679. OV.Add(m, OV.NewMenu('&Open...', '', 0, NIL));
  680. (*!TODO*) m.children.prev.status := OV.disabled;
  681. OV.Add(m, OV.NewMenu('&Save', 'fo.ini', 0, NIL));
  682. (*!TODO*) m.children.prev.status := OV.disabled;
  683. OV.Add(m, OV.NewMenu('Save &as...', '', 0, NIL));
  684. (*!TODO*) m.children.prev.status := OV.disabled;
  685. OV.AddMenu(app, m);
  686. m := OV.NewMenu('&Window', '', 0, NIL);
  687. OV.Add(m, OV.NewMenu('&Tile', '', 0, NIL));
  688. (*!TODO*) m.children.prev.status := OV.disabled;
  689. OV.Add(m, OV.NewMenu('C&ascade', '', 0, NIL));
  690. (*!TODO*) m.children.prev.status := OV.disabled;
  691. OV.Add(m, OV.NewMenu('Cl&ose all', '', 0, NIL));
  692. (*!TODO*) m.children.prev.status := OV.disabled;
  693. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  694. OV.Add(m, OV.NewMenu('&Size/Move', 'Ctrl+F5', OV.hCtrlF5, NIL));
  695. OV.Add(m, OV.NewMenu('&Zoom', 'F5', OV.hF5, OV.ZoomCurWindow));
  696. OV.Add(m, OV.NewMenu('&Next', 'F6', OV.hF6, OV.NextWindow));
  697. OV.Add(m, OV.NewMenu('&Previous', 'Shift+F6', OV.hShiftF6, OV.PrevWindow));
  698. OV.Add(m, OV.NewMenu('&Close', 'Alt+F3', OV.hAltF3, OV.CloseCurWindow));
  699. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  700. OV.Add(m, OV.NewMenu('&List...', 'Alt+0', OV.hAlt0, NIL));
  701. (*!TODO*) m.children.prev.status := OV.disabled;
  702. OV.Add(m, OV.NewMenu('&Refresh display', '', 0, OV.RefreshDisplay));
  703. OV.AddMenu(app, m);
  704. m := OV.NewMenu('&Help', '', 0, NIL);
  705. OV.Add(m, OV.NewMenu('&Contents', '', 0, NIL));
  706. (*!TODO*) m.children.prev.status := OV.disabled;
  707. OV.Add(m, OV.NewMenu('&Index', 'Shift+F1', OV.hShiftF1, NIL));
  708. (*!TODO*) m.children.prev.status := OV.disabled;
  709. OV.Add(m, OV.NewMenu('&Topic search', 'Ctrl+F1', OV.hCtrlF1, NIL));
  710. (*!TODO*) m.children.prev.status := OV.disabled;
  711. OV.Add(m, OV.NewMenu('&Previous topic', 'Alt+F1', OV.hAltF1, NIL));
  712. (*!TODO*) m.children.prev.status := OV.disabled;
  713. OV.Add(m, OV.NewMenu('&Using help', '', 0, NIL));
  714. (*!TODO*) m.children.prev.status := OV.disabled;
  715. OV.Add(m, OV.NewMenu('&Files...', '', 0, NIL));
  716. (*!TODO*) m.children.prev.status := OV.disabled;
  717. OV.Add(m, OV.NewMenu('-', '', 0, NIL));
  718. OV.Add(m, OV.NewMenu('&About...', '', OV.hF1, HelpAbout));
  719. OV.AddMenu(app, m);
  720. OV.AddStatusbar(app, OV.NewQuickBtn('Help', 'F1', 0, HelpAbout));
  721. OV.AddStatusbar(app, OV.NewQuickBtn('Save', 'F2', 0, FileSave));
  722. OV.AddStatusbar(app, OV.NewQuickBtn('Open', 'F3', 0, FileOpen));
  723. OV.AddStatusbar(app, OV.NewQuickBtn('Compile & Run', 'F9', 0, OnBuild));
  724. OV.AddStatusbar(app, OV.NewQuickBtn('Local menu', 'Alt+F10', 0, NIL))
  725. END InitIDE;
  726. PROCEDURE Init(): BOOLEAN;
  727. VAR success: BOOLEAN;
  728. BEGIN
  729. success := FALSE;
  730. IF T.Init(needFullscreen) THEN
  731. InitIDE;
  732. needWindowed := TRUE;
  733. blockToggle := FALSE;
  734. success := TRUE
  735. ELSE Out.String('Terminal init failed.'); Out.Ln
  736. END;
  737. RETURN success
  738. END Init;
  739. BEGIN
  740. IF ~Init() THEN Out.String('Could not initialize.'); Out.Ln
  741. ELSE OV.RunApp(app)
  742. END;
  743. G.Close
  744. END FreeOberon.