AmberCli.st 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. Smalltalk current createPackage: 'AmberCli'!
  2. Object subclass: #AmberCli
  3. instanceVariableNames: ''
  4. package: 'AmberCli'!
  5. !AmberCli commentStamp!
  6. I am the Amber CLI (CommandLine Interface) tool which runs on Node.js.
  7. My responsibility is to start different Amber programs like the FileServer or the Repl.
  8. Which program to start is determined by the first commandline parameters passed to the AmberCli executable.
  9. Use `help` to get a list of all available options.
  10. Any further commandline parameters are passed to the specific program.
  11. ## Commands
  12. New commands can be added by creating a class side method in the `commands` protocol which takes one parameter.
  13. This parameter is an array of all commandline options + values passed on to the program.
  14. Any `camelCaseCommand` is transformed into a commandline parameter of the form `camel-case-command` and vice versa.!
  15. !AmberCli class methodsFor: 'commandline'!
  16. commandLineSwitches
  17. "Collect all methodnames from the 'commands' protocol of the class
  18. and select the ones with only one parameter.
  19. Then remove the ':' at the end of the name.
  20. Additionally all uppercase letters are made lowercase and preceded by a '-'.
  21. Example: fallbackPage: becomes --fallback-page.
  22. Return the Array containing the commandline switches."
  23. | switches |
  24. switches := ((self class methodsInProtocol: 'commands') collect: [ :each | each selector]).
  25. switches := switches select: [ :each | each match: '^[^:]*:$'].
  26. switches :=switches collect: [ :each |
  27. (each allButLast replace: '([A-Z])' with: '-$1') asLowercase].
  28. ^switches
  29. !
  30. handleArguments: args
  31. | selector |
  32. selector := self selectorForCommandLineSwitch: (args first).
  33. args remove: args first.
  34. self perform: selector withArguments: (Array with: args)
  35. !
  36. selectorForCommandLineSwitch: aSwitch
  37. "Add ':' at the end and replace all occurences of a lowercase letter preceded by a '-' with the Uppercase letter.
  38. Example: fallback-page becomes fallbackPage:.
  39. If no correct selector is found return 'help:'"
  40. | command selector |
  41. (self commandLineSwitches includes: aSwitch)
  42. ifTrue: [ selector := (aSwitch replace: '-[a-z]' with: [ :each | each second asUppercase ]), ':']
  43. ifFalse: [ selector := 'help:' ].
  44. ^selector
  45. ! !
  46. !AmberCli class methodsFor: 'commands'!
  47. help: args
  48. console log: 'Available Commands:'.
  49. self commandLineSwitches do: [ :each | console log: each ]
  50. !
  51. repl: args
  52. ^Repl new createInterface
  53. !
  54. serve: args
  55. ^(FileServer createServerWithArguments: args) start
  56. ! !
  57. !AmberCli class methodsFor: 'startup'!
  58. main
  59. "Main entry point for Amber applications.
  60. Parses commandline arguments and starts the according subprogram."
  61. | args nodeMinorVersion |
  62. nodeMinorVersion := ((process version) tokenize: '.') second asNumber.
  63. nodeMinorVersion < 8 ifTrue: [
  64. console log: 'You are currently using Node.js ', (process version).
  65. console log: 'Required is at least Node.js v0.8.x or greater.'.
  66. ^ -1.
  67. ].
  68. args := process argv.
  69. "Remove the first args which contain the path to the node executable and the script file."
  70. args removeFrom: 1 to: 2.
  71. (args isEmpty)
  72. ifTrue: [self help: nil]
  73. ifFalse: [^self handleArguments: args]
  74. ! !
  75. Object subclass: #FileServer
  76. instanceVariableNames: 'path http fs url host port basePath util username password fallbackPage'
  77. package: 'AmberCli'!
  78. !FileServer commentStamp!
  79. I am the Amber Smalltalk FileServer.
  80. My runtime requirement is a functional Node.js executable.
  81. To start a FileServer instance on port `4000` use the following code:
  82. FileServer new start
  83. A parameterized instance can be created with the following code:
  84. FileServer createServerWithArguments: options
  85. Here, `options` is an array of commandline style strings each followed by a value e.g. `#('--port', '6000', '--host', '0.0.0.0')`.
  86. A list of all available parameters can be printed to the commandline by passing `--help` as parameter.
  87. See the `Options` section for further details on how options are mapped to instance methods.
  88. After startup FileServer checks if the directory layout required by Amber is present and logs a warning on absence.
  89. ## Options
  90. Each option is of the form `--some-option-string` which is transformed into a selector of the format `someOptionString:`.
  91. The trailing `--` gets removed, each `-[a-z]` gets transformed into the according uppercase letter, and a `:` is appended to create a selector which takes a single argument.
  92. Afterwards, the selector gets executed on the `FileServer` instance with the value following in the options array as parameter.
  93. ## Adding new commandline parameters
  94. Adding new commandline parameters to `FileServer` is as easy as adding a new single parameter method to the `accessing` protocol.!
  95. !FileServer methodsFor: 'accessing'!
  96. basePath
  97. ^basePath ifNil: ['./']
  98. !
  99. basePath: aString
  100. basePath := aString
  101. !
  102. fallbackPage
  103. ^fallbackPage
  104. !
  105. fallbackPage: aString
  106. fallbackPage := aString
  107. !
  108. host
  109. ^host
  110. !
  111. host: hostname
  112. host := hostname
  113. !
  114. password: aPassword
  115. password := aPassword.
  116. !
  117. port
  118. ^port
  119. !
  120. port: aNumber
  121. port := aNumber
  122. !
  123. username: aUsername
  124. username := aUsername.
  125. ! !
  126. !FileServer methodsFor: 'initialization'!
  127. checkDirectoryLayout
  128. (fs existsSync: self basePath, 'index.html') ifFalse: [
  129. console warn: 'Warning: project directory does not contain index.html'].
  130. (fs existsSync: self basePath, 'st') ifFalse: [
  131. console warn: 'Warning: project directory is missing an "st" directory'].
  132. (fs existsSync: self basePath, 'js') ifFalse: [
  133. console warn: 'Warning: project directory is missing a "js" directory'].
  134. !
  135. initialize
  136. super initialize.
  137. path := self require: 'path'.
  138. http := self require: 'http'.
  139. fs := self require: 'fs'.
  140. util := self require: 'util'.
  141. url := self require: 'url'.
  142. host := self class defaultHost.
  143. port := self class defaultPort.
  144. username := nil.
  145. password := nil.
  146. fallbackPage := nil.
  147. ! !
  148. !FileServer methodsFor: 'private'!
  149. base64Decode: aString
  150. <return (new Buffer(aString, 'base64').toString())>
  151. !
  152. isAuthenticated: aRequest
  153. "Basic HTTP Auth: http://stackoverflow.com/a/5957629/293175
  154. and https://gist.github.com/1686663"
  155. | header token auth parts|
  156. (username isNil and: [password isNil]) ifTrue: [^true].
  157. "get authentication header"
  158. header := (aRequest headers at: 'authorization') ifNil:[''].
  159. (header isEmpty)
  160. ifTrue: [^false]
  161. ifFalse: [
  162. "get authentication token"
  163. token := (header tokenize: ' ') ifNil:[''].
  164. "convert back from base64"
  165. auth := self base64Decode: (token at: 2).
  166. "split token at colon"
  167. parts := auth tokenize: ':'.
  168. ((username = (parts at: 1)) and: [password = (parts at: 2)])
  169. ifTrue: [^true]
  170. ifFalse: [^false]
  171. ].
  172. !
  173. require: aModuleString
  174. "call to the require function"
  175. ^require value: aModuleString
  176. !
  177. writeData: data toFileNamed: aFilename
  178. console log: aFilename
  179. ! !
  180. !FileServer methodsFor: 'request handling'!
  181. handleGETRequest: aRequest respondTo: aResponse
  182. | uri filename |
  183. uri := (url parse: aRequest url) pathname.
  184. filename := path join: self basePath with: uri.
  185. fs exists: filename do: [:aBoolean |
  186. aBoolean
  187. ifFalse: [self respondNotFoundTo: aResponse]
  188. ifTrue: [self respondFileNamed: filename to: aResponse]]
  189. !
  190. handleOPTIONSRequest: aRequest respondTo: aResponse
  191. aResponse writeHead: 200 options: #{'Access-Control-Allow-Origin' -> '*'.
  192. 'Access-Control-Allow-Methods' -> 'GET, PUT, POST, DELETE, OPTIONS'.
  193. 'Access-Control-Allow-Headers' -> 'Content-Type, Accept'.
  194. 'Content-Length' -> 0.
  195. 'Access-Control-Max-Age' -> 10}.
  196. aResponse end
  197. !
  198. handlePUTRequest: aRequest respondTo: aResponse
  199. | file stream |
  200. (self isAuthenticated: aRequest)
  201. ifFalse: [self respondAuthenticationRequiredTo: aResponse. ^nil].
  202. file := '.', aRequest url.
  203. stream := fs createWriteStream: file.
  204. stream on: 'error' do: [:error |
  205. console warn: 'Error creating WriteStream for file ', file.
  206. console warn: ' Did you forget to create the necessary js/ or st/ directory in your project?'.
  207. console warn: ' The exact error is: ', error.
  208. self respondNotCreatedTo: aResponse].
  209. stream on: 'close' do: [
  210. self respondCreatedTo: aResponse].
  211. aRequest setEncoding: 'utf8'.
  212. aRequest on: 'data' do: [:data |
  213. stream write: data].
  214. aRequest on: 'end' do: [
  215. stream writable ifTrue: [stream end]]
  216. !
  217. handleRequest: aRequest respondTo: aResponse
  218. aRequest method = 'PUT'
  219. ifTrue: [self handlePUTRequest: aRequest respondTo: aResponse].
  220. aRequest method = 'GET'
  221. ifTrue:[self handleGETRequest: aRequest respondTo: aResponse].
  222. aRequest method = 'OPTIONS'
  223. ifTrue:[self handleOPTIONSRequest: aRequest respondTo: aResponse]
  224. !
  225. respondAuthenticationRequiredTo: aResponse
  226. aResponse
  227. writeHead: 401 options: #{'WWW-Authenticate' -> 'Basic realm="Secured Developer Area"'};
  228. write: '<html><body>Authentication needed</body></html>';
  229. end.
  230. !
  231. respondCreatedTo: aResponse
  232. aResponse
  233. writeHead: 201 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};
  234. end.
  235. !
  236. respondFileNamed: aFilename to: aResponse
  237. | type filename |
  238. filename := aFilename.
  239. (fs statSync: aFilename) isDirectory ifTrue: [
  240. filename := filename, 'index.html'].
  241. fs readFile: filename do: [:ex :file |
  242. ex notNil
  243. ifTrue: [
  244. console log: filename, ' does not exist'.
  245. self respondInternalErrorTo: aResponse]
  246. ifFalse: [
  247. type := self class mimeTypeFor: filename.
  248. type = 'application/javascript'
  249. ifTrue: [ type:=type,';charset=utf-8' ].
  250. aResponse
  251. writeHead: 200 options: #{'Content-Type' -> type};
  252. write: file encoding: 'binary';
  253. end]]
  254. !
  255. respondInternalErrorTo: aResponse
  256. aResponse
  257. writeHead: 500 options: #{'Content-Type' -> 'text/plain'};
  258. write: '500 Internal server error';
  259. end
  260. !
  261. respondNotCreatedTo: aResponse
  262. aResponse
  263. writeHead: 400 options: #{'Content-Type' -> 'text/plain'};
  264. write: 'File could not be created. Did you forget to create the st/js directories on the server?';
  265. end.
  266. !
  267. respondNotFoundTo: aResponse
  268. self fallbackPage isNil ifFalse: [^self respondFileNamed: self fallbackPage to: aResponse].
  269. aResponse
  270. writeHead: 404 options: #{'Content-Type' -> 'text/plain'};
  271. write: '404 Not found';
  272. end
  273. !
  274. respondOKTo: aResponse
  275. aResponse
  276. writeHead: 200 options: #{'Content-Type' -> 'text/plain'. 'Access-Control-Allow-Origin' -> '*'};
  277. end.
  278. ! !
  279. !FileServer methodsFor: 'starting'!
  280. start
  281. "Checks if required directory layout is present (issue warning if not).
  282. Afterwards start the server."
  283. self checkDirectoryLayout.
  284. (http createServer: [:request :response |
  285. self handleRequest: request respondTo: response])
  286. on: 'error' do: [:error | console log: 'Error starting server: ', error];
  287. on: 'listening' do: [console log: 'Starting file server on http://', self host, ':', self port asString];
  288. listen: self port host: self host.
  289. !
  290. startOn: aPort
  291. self port: aPort.
  292. self start
  293. ! !
  294. FileServer class instanceVariableNames: 'mimeTypes'!
  295. !FileServer class methodsFor: 'accessing'!
  296. commandLineSwitches
  297. "Collect all methodnames from the 'accessing' protocol
  298. and select the ones with only one parameter.
  299. Then remove the ':' at the end of the name
  300. and add a '--' at the beginning.
  301. Additionally all uppercase letters are made lowercase and preceded by a '-'.
  302. Example: fallbackPage: becomes --fallback-page.
  303. Return the Array containing the commandline switches."
  304. | switches |
  305. switches := ((self methodsInProtocol: 'accessing') collect: [ :each | each selector]).
  306. switches := switches select: [ :each | each match: '^[^:]*:$'].
  307. switches :=switches collect: [ :each |
  308. (each allButLast replace: '([A-Z])' with: '-$1') asLowercase replace: '^([a-z])' with: '--$1' ].
  309. ^switches
  310. !
  311. defaultHost
  312. ^'127.0.0.1'
  313. !
  314. defaultMimeTypes
  315. ^ #{
  316. '%' -> 'application/x-trash'.
  317. '323' -> 'text/h323'.
  318. 'abw' -> 'application/x-abiword'.
  319. 'ai' -> 'application/postscript'.
  320. 'aif' -> 'audio/x-aiff'.
  321. 'aifc' -> 'audio/x-aiff'.
  322. 'aiff' -> 'audio/x-aiff'.
  323. 'alc' -> 'chemical/x-alchemy'.
  324. 'art' -> 'image/x-jg'.
  325. 'asc' -> 'text/plain'.
  326. 'asf' -> 'video/x-ms-asf'.
  327. 'asn' -> 'chemical/x-ncbi-asn1-spec'.
  328. 'aso' -> 'chemical/x-ncbi-asn1-binary'.
  329. 'asx' -> 'video/x-ms-asf'.
  330. 'au' -> 'audio/basic'.
  331. 'avi' -> 'video/x-msvideo'.
  332. 'b' -> 'chemical/x-molconn-Z'.
  333. 'bak' -> 'application/x-trash'.
  334. 'bat' -> 'application/x-msdos-program'.
  335. 'bcpio' -> 'application/x-bcpio'.
  336. 'bib' -> 'text/x-bibtex'.
  337. 'bin' -> 'application/octet-stream'.
  338. 'bmp' -> 'image/x-ms-bmp'.
  339. 'book' -> 'application/x-maker'.
  340. 'bsd' -> 'chemical/x-crossfire'.
  341. 'c' -> 'text/x-csrc'.
  342. 'c++' -> 'text/x-c++src'.
  343. 'c3d' -> 'chemical/x-chem3d'.
  344. 'cac' -> 'chemical/x-cache'.
  345. 'cache' -> 'chemical/x-cache'.
  346. 'cascii' -> 'chemical/x-cactvs-binary'.
  347. 'cat' -> 'application/vnd.ms-pki.seccat'.
  348. 'cbin' -> 'chemical/x-cactvs-binary'.
  349. 'cc' -> 'text/x-c++src'.
  350. 'cdf' -> 'application/x-cdf'.
  351. 'cdr' -> 'image/x-coreldraw'.
  352. 'cdt' -> 'image/x-coreldrawtemplate'.
  353. 'cdx' -> 'chemical/x-cdx'.
  354. 'cdy' -> 'application/vnd.cinderella'.
  355. 'cef' -> 'chemical/x-cxf'.
  356. 'cer' -> 'chemical/x-cerius'.
  357. 'chm' -> 'chemical/x-chemdraw'.
  358. 'chrt' -> 'application/x-kchart'.
  359. 'cif' -> 'chemical/x-cif'.
  360. 'class' -> 'application/java-vm'.
  361. 'cls' -> 'text/x-tex'.
  362. 'cmdf' -> 'chemical/x-cmdf'.
  363. 'cml' -> 'chemical/x-cml'.
  364. 'cod' -> 'application/vnd.rim.cod'.
  365. 'com' -> 'application/x-msdos-program'.
  366. 'cpa' -> 'chemical/x-compass'.
  367. 'cpio' -> 'application/x-cpio'.
  368. 'cpp' -> 'text/x-c++src'.
  369. 'cpt' -> 'image/x-corelphotopaint'.
  370. 'crl' -> 'application/x-pkcs7-crl'.
  371. 'crt' -> 'application/x-x509-ca-cert'.
  372. 'csf' -> 'chemical/x-cache-csf'.
  373. 'csh' -> 'text/x-csh'.
  374. 'csm' -> 'chemical/x-csml'.
  375. 'csml' -> 'chemical/x-csml'.
  376. 'css' -> 'text/css'.
  377. 'csv' -> 'text/comma-separated-values'.
  378. 'ctab' -> 'chemical/x-cactvs-binary'.
  379. 'ctx' -> 'chemical/x-ctx'.
  380. 'cu' -> 'application/cu-seeme'.
  381. 'cub' -> 'chemical/x-gaussian-cube'.
  382. 'cxf' -> 'chemical/x-cxf'.
  383. 'cxx' -> 'text/x-c++src'.
  384. 'dat' -> 'chemical/x-mopac-input'.
  385. 'dcr' -> 'application/x-director'.
  386. 'deb' -> 'application/x-debian-package'.
  387. 'dif' -> 'video/dv'.
  388. 'diff' -> 'text/plain'.
  389. 'dir' -> 'application/x-director'.
  390. 'djv' -> 'image/vnd.djvu'.
  391. 'djvu' -> 'image/vnd.djvu'.
  392. 'dl' -> 'video/dl'.
  393. 'dll' -> 'application/x-msdos-program'.
  394. 'dmg' -> 'application/x-apple-diskimage'.
  395. 'dms' -> 'application/x-dms'.
  396. 'doc' -> 'application/msword'.
  397. 'dot' -> 'application/msword'.
  398. 'dv' -> 'video/dv'.
  399. 'dvi' -> 'application/x-dvi'.
  400. 'dx' -> 'chemical/x-jcamp-dx'.
  401. 'dxr' -> 'application/x-director'.
  402. 'emb' -> 'chemical/x-embl-dl-nucleotide'.
  403. 'embl' -> 'chemical/x-embl-dl-nucleotide'.
  404. 'ent' -> 'chemical/x-pdb'.
  405. 'eps' -> 'application/postscript'.
  406. 'etx' -> 'text/x-setext'.
  407. 'exe' -> 'application/x-msdos-program'.
  408. 'ez' -> 'application/andrew-inset'.
  409. 'fb' -> 'application/x-maker'.
  410. 'fbdoc' -> 'application/x-maker'.
  411. 'fch' -> 'chemical/x-gaussian-checkpoint'.
  412. 'fchk' -> 'chemical/x-gaussian-checkpoint'.
  413. 'fig' -> 'application/x-xfig'.
  414. 'flac' -> 'application/x-flac'.
  415. 'fli' -> 'video/fli'.
  416. 'fm' -> 'application/x-maker'.
  417. 'frame' -> 'application/x-maker'.
  418. 'frm' -> 'application/x-maker'.
  419. 'gal' -> 'chemical/x-gaussian-log'.
  420. 'gam' -> 'chemical/x-gamess-input'.
  421. 'gamin' -> 'chemical/x-gamess-input'.
  422. 'gau' -> 'chemical/x-gaussian-input'.
  423. 'gcd' -> 'text/x-pcs-gcd'.
  424. 'gcf' -> 'application/x-graphing-calculator'.
  425. 'gcg' -> 'chemical/x-gcg8-sequence'.
  426. 'gen' -> 'chemical/x-genbank'.
  427. 'gf' -> 'application/x-tex-gf'.
  428. 'gif' -> 'image/gif'.
  429. 'gjc' -> 'chemical/x-gaussian-input'.
  430. 'gjf' -> 'chemical/x-gaussian-input'.
  431. 'gl' -> 'video/gl'.
  432. 'gnumeric' -> 'application/x-gnumeric'.
  433. 'gpt' -> 'chemical/x-mopac-graph'.
  434. 'gsf' -> 'application/x-font'.
  435. 'gsm' -> 'audio/x-gsm'.
  436. 'gtar' -> 'application/x-gtar'.
  437. 'h' -> 'text/x-chdr'.
  438. 'h++' -> 'text/x-c++hdr'.
  439. 'hdf' -> 'application/x-hdf'.
  440. 'hh' -> 'text/x-c++hdr'.
  441. 'hin' -> 'chemical/x-hin'.
  442. 'hpp' -> 'text/x-c++hdr'.
  443. 'hqx' -> 'application/mac-binhex40'.
  444. 'hs' -> 'text/x-haskell'.
  445. 'hta' -> 'application/hta'.
  446. 'htc' -> 'text/x-component'.
  447. 'htm' -> 'text/html'.
  448. 'html' -> 'text/html'.
  449. 'hxx' -> 'text/x-c++hdr'.
  450. 'ica' -> 'application/x-ica'.
  451. 'ice' -> 'x-conference/x-cooltalk'.
  452. 'ico' -> 'image/x-icon'.
  453. 'ics' -> 'text/calendar'.
  454. 'icz' -> 'text/calendar'.
  455. 'ief' -> 'image/ief'.
  456. 'iges' -> 'model/iges'.
  457. 'igs' -> 'model/iges'.
  458. 'iii' -> 'application/x-iphone'.
  459. 'inp' -> 'chemical/x-gamess-input'.
  460. 'ins' -> 'application/x-internet-signup'.
  461. 'iso' -> 'application/x-iso9660-image'.
  462. 'isp' -> 'application/x-internet-signup'.
  463. 'ist' -> 'chemical/x-isostar'.
  464. 'istr' -> 'chemical/x-isostar'.
  465. 'jad' -> 'text/vnd.sun.j2me.app-descriptor'.
  466. 'jar' -> 'application/java-archive'.
  467. 'java' -> 'text/x-java'.
  468. 'jdx' -> 'chemical/x-jcamp-dx'.
  469. 'jmz' -> 'application/x-jmol'.
  470. 'jng' -> 'image/x-jng'.
  471. 'jnlp' -> 'application/x-java-jnlp-file'.
  472. 'jpe' -> 'image/jpeg'.
  473. 'jpeg' -> 'image/jpeg'.
  474. 'jpg' -> 'image/jpeg'.
  475. 'js' -> 'application/javascript'.
  476. 'kar' -> 'audio/midi'.
  477. 'key' -> 'application/pgp-keys'.
  478. 'kil' -> 'application/x-killustrator'.
  479. 'kin' -> 'chemical/x-kinemage'.
  480. 'kpr' -> 'application/x-kpresenter'.
  481. 'kpt' -> 'application/x-kpresenter'.
  482. 'ksp' -> 'application/x-kspread'.
  483. 'kwd' -> 'application/x-kword'.
  484. 'kwt' -> 'application/x-kword'.
  485. 'latex' -> 'application/x-latex'.
  486. 'lha' -> 'application/x-lha'.
  487. 'lhs' -> 'text/x-literate-haskell'.
  488. 'lsf' -> 'video/x-la-asf'.
  489. 'lsx' -> 'video/x-la-asf'.
  490. 'ltx' -> 'text/x-tex'.
  491. 'lzh' -> 'application/x-lzh'.
  492. 'lzx' -> 'application/x-lzx'.
  493. 'm3u' -> 'audio/x-mpegurl'.
  494. 'm4a' -> 'audio/mpeg'.
  495. 'maker' -> 'application/x-maker'.
  496. 'man' -> 'application/x-troff-man'.
  497. 'mcif' -> 'chemical/x-mmcif'.
  498. 'mcm' -> 'chemical/x-macmolecule'.
  499. 'mdb' -> 'application/msaccess'.
  500. 'me' -> 'application/x-troff-me'.
  501. 'mesh' -> 'model/mesh'.
  502. 'mid' -> 'audio/midi'.
  503. 'midi' -> 'audio/midi'.
  504. 'mif' -> 'application/x-mif'.
  505. 'mm' -> 'application/x-freemind'.
  506. 'mmd' -> 'chemical/x-macromodel-input'.
  507. 'mmf' -> 'application/vnd.smaf'.
  508. 'mml' -> 'text/mathml'.
  509. 'mmod' -> 'chemical/x-macromodel-input'.
  510. 'mng' -> 'video/x-mng'.
  511. 'moc' -> 'text/x-moc'.
  512. 'mol' -> 'chemical/x-mdl-molfile'.
  513. 'mol2' -> 'chemical/x-mol2'.
  514. 'moo' -> 'chemical/x-mopac-out'.
  515. 'mop' -> 'chemical/x-mopac-input'.
  516. 'mopcrt' -> 'chemical/x-mopac-input'.
  517. 'mov' -> 'video/quicktime'.
  518. 'movie' -> 'video/x-sgi-movie'.
  519. 'mp2' -> 'audio/mpeg'.
  520. 'mp3' -> 'audio/mpeg'.
  521. 'mp4' -> 'video/mp4'.
  522. 'mpc' -> 'chemical/x-mopac-input'.
  523. 'mpe' -> 'video/mpeg'.
  524. 'mpeg' -> 'video/mpeg'.
  525. 'mpega' -> 'audio/mpeg'.
  526. 'mpg' -> 'video/mpeg'.
  527. 'mpga' -> 'audio/mpeg'.
  528. 'ms' -> 'application/x-troff-ms'.
  529. 'msh' -> 'model/mesh'.
  530. 'msi' -> 'application/x-msi'.
  531. 'mvb' -> 'chemical/x-mopac-vib'.
  532. 'mxu' -> 'video/vnd.mpegurl'.
  533. 'nb' -> 'application/mathematica'.
  534. 'nc' -> 'application/x-netcdf'.
  535. 'nwc' -> 'application/x-nwc'.
  536. 'o' -> 'application/x-object'.
  537. 'oda' -> 'application/oda'.
  538. 'odb' -> 'application/vnd.oasis.opendocument.database'.
  539. 'odc' -> 'application/vnd.oasis.opendocument.chart'.
  540. 'odf' -> 'application/vnd.oasis.opendocument.formula'.
  541. 'odg' -> 'application/vnd.oasis.opendocument.graphics'.
  542. 'odi' -> 'application/vnd.oasis.opendocument.image'.
  543. 'odm' -> 'application/vnd.oasis.opendocument.text-master'.
  544. 'odp' -> 'application/vnd.oasis.opendocument.presentation'.
  545. 'ods' -> 'application/vnd.oasis.opendocument.spreadsheet'.
  546. 'odt' -> 'application/vnd.oasis.opendocument.text'.
  547. 'ogg' -> 'application/ogg'.
  548. 'old' -> 'application/x-trash'.
  549. 'oth' -> 'application/vnd.oasis.opendocument.text-web'.
  550. 'oza' -> 'application/x-oz-application'.
  551. 'p' -> 'text/x-pascal'.
  552. 'p7r' -> 'application/x-pkcs7-certreqresp'.
  553. 'pac' -> 'application/x-ns-proxy-autoconfig'.
  554. 'pas' -> 'text/x-pascal'.
  555. 'pat' -> 'image/x-coreldrawpattern'.
  556. 'pbm' -> 'image/x-portable-bitmap'.
  557. 'pcf' -> 'application/x-font'.
  558. 'pcf.Z' -> 'application/x-font'.
  559. 'pcx' -> 'image/pcx'.
  560. 'pdb' -> 'chemical/x-pdb'.
  561. 'pdf' -> 'application/pdf'.
  562. 'pfa' -> 'application/x-font'.
  563. 'pfb' -> 'application/x-font'.
  564. 'pgm' -> 'image/x-portable-graymap'.
  565. 'pgn' -> 'application/x-chess-pgn'.
  566. 'pgp' -> 'application/pgp-signature'.
  567. 'pk' -> 'application/x-tex-pk'.
  568. 'pl' -> 'text/x-perl'.
  569. 'pls' -> 'audio/x-scpls'.
  570. 'pm' -> 'text/x-perl'.
  571. 'png' -> 'image/png'.
  572. 'pnm' -> 'image/x-portable-anymap'.
  573. 'pot' -> 'text/plain'.
  574. 'ppm' -> 'image/x-portable-pixmap'.
  575. 'pps' -> 'application/vnd.ms-powerpoint'.
  576. 'ppt' -> 'application/vnd.ms-powerpoint'.
  577. 'prf' -> 'application/pics-rules'.
  578. 'prt' -> 'chemical/x-ncbi-asn1-ascii'.
  579. 'ps' -> 'application/postscript'.
  580. 'psd' -> 'image/x-photoshop'.
  581. 'psp' -> 'text/x-psp'.
  582. 'py' -> 'text/x-python'.
  583. 'pyc' -> 'application/x-python-code'.
  584. 'pyo' -> 'application/x-python-code'.
  585. 'qt' -> 'video/quicktime'.
  586. 'qtl' -> 'application/x-quicktimeplayer'.
  587. 'ra' -> 'audio/x-realaudio'.
  588. 'ram' -> 'audio/x-pn-realaudio'.
  589. 'rar' -> 'application/rar'.
  590. 'ras' -> 'image/x-cmu-raster'.
  591. 'rd' -> 'chemical/x-mdl-rdfile'.
  592. 'rdf' -> 'application/rdf+xml'.
  593. 'rgb' -> 'image/x-rgb'.
  594. 'rm' -> 'audio/x-pn-realaudio'.
  595. 'roff' -> 'application/x-troff'.
  596. 'ros' -> 'chemical/x-rosdal'.
  597. 'rpm' -> 'application/x-redhat-package-manager'.
  598. 'rss' -> 'application/rss+xml'.
  599. 'rtf' -> 'text/rtf'.
  600. 'rtx' -> 'text/richtext'.
  601. 'rxn' -> 'chemical/x-mdl-rxnfile'.
  602. 'sct' -> 'text/scriptlet'.
  603. 'sd' -> 'chemical/x-mdl-sdfile'.
  604. 'sd2' -> 'audio/x-sd2'.
  605. 'sda' -> 'application/vnd.stardivision.draw'.
  606. 'sdc' -> 'application/vnd.stardivision.calc'.
  607. 'sdd' -> 'application/vnd.stardivision.impress'.
  608. 'sdf' -> 'chemical/x-mdl-sdfile'.
  609. 'sdp' -> 'application/vnd.stardivision.impress'.
  610. 'sdw' -> 'application/vnd.stardivision.writer'.
  611. 'ser' -> 'application/java-serialized-object'.
  612. 'sgf' -> 'application/x-go-sgf'.
  613. 'sgl' -> 'application/vnd.stardivision.writer-global'.
  614. 'sh' -> 'text/x-sh'.
  615. 'shar' -> 'application/x-shar'.
  616. 'shtml' -> 'text/html'.
  617. 'sid' -> 'audio/prs.sid'.
  618. 'sik' -> 'application/x-trash'.
  619. 'silo' -> 'model/mesh'.
  620. 'sis' -> 'application/vnd.symbian.install'.
  621. 'sit' -> 'application/x-stuffit'.
  622. 'skd' -> 'application/x-koan'.
  623. 'skm' -> 'application/x-koan'.
  624. 'skp' -> 'application/x-koan'.
  625. 'skt' -> 'application/x-koan'.
  626. 'smf' -> 'application/vnd.stardivision.math'.
  627. 'smi' -> 'application/smil'.
  628. 'smil' -> 'application/smil'.
  629. 'snd' -> 'audio/basic'.
  630. 'spc' -> 'chemical/x-galactic-spc'.
  631. 'spl' -> 'application/x-futuresplash'.
  632. 'src' -> 'application/x-wais-source'.
  633. 'stc' -> 'application/vnd.sun.xml.calc.template'.
  634. 'std' -> 'application/vnd.sun.xml.draw.template'.
  635. 'sti' -> 'application/vnd.sun.xml.impress.template'.
  636. 'stl' -> 'application/vnd.ms-pki.stl'.
  637. 'stw' -> 'application/vnd.sun.xml.writer.template'.
  638. 'sty' -> 'text/x-tex'.
  639. 'sv4cpio' -> 'application/x-sv4cpio'.
  640. 'sv4crc' -> 'application/x-sv4crc'.
  641. 'svg' -> 'image/svg+xml'.
  642. 'svgz' -> 'image/svg+xml'.
  643. 'sw' -> 'chemical/x-swissprot'.
  644. 'swf' -> 'application/x-shockwave-flash'.
  645. 'swfl' -> 'application/x-shockwave-flash'.
  646. 'sxc' -> 'application/vnd.sun.xml.calc'.
  647. 'sxd' -> 'application/vnd.sun.xml.draw'.
  648. 'sxg' -> 'application/vnd.sun.xml.writer.global'.
  649. 'sxi' -> 'application/vnd.sun.xml.impress'.
  650. 'sxm' -> 'application/vnd.sun.xml.math'.
  651. 'sxw' -> 'application/vnd.sun.xml.writer'.
  652. 't' -> 'application/x-troff'.
  653. 'tar' -> 'application/x-tar'.
  654. 'taz' -> 'application/x-gtar'.
  655. 'tcl' -> 'text/x-tcl'.
  656. 'tex' -> 'text/x-tex'.
  657. 'texi' -> 'application/x-texinfo'.
  658. 'texinfo' -> 'application/x-texinfo'.
  659. 'text' -> 'text/plain'.
  660. 'tgf' -> 'chemical/x-mdl-tgf'.
  661. 'tgz' -> 'application/x-gtar'.
  662. 'tif' -> 'image/tiff'.
  663. 'tiff' -> 'image/tiff'.
  664. 'tk' -> 'text/x-tcl'.
  665. 'tm' -> 'text/texmacs'.
  666. 'torrent' -> 'application/x-bittorrent'.
  667. 'tr' -> 'application/x-troff'.
  668. 'ts' -> 'text/texmacs'.
  669. 'tsp' -> 'application/dsptype'.
  670. 'tsv' -> 'text/tab-separated-values'.
  671. 'txt' -> 'text/plain'.
  672. 'udeb' -> 'application/x-debian-package'.
  673. 'uls' -> 'text/iuls'.
  674. 'ustar' -> 'application/x-ustar'.
  675. 'val' -> 'chemical/x-ncbi-asn1-binary'.
  676. 'vcd' -> 'application/x-cdlink'.
  677. 'vcf' -> 'text/x-vcard'.
  678. 'vcs' -> 'text/x-vcalendar'.
  679. 'vmd' -> 'chemical/x-vmd'.
  680. 'vms' -> 'chemical/x-vamas-iso14976'.
  681. 'vor' -> 'application/vnd.stardivision.writer'.
  682. 'vrm' -> 'x-world/x-vrml'.
  683. 'vrml' -> 'x-world/x-vrml'.
  684. 'vsd' -> 'application/vnd.visio'.
  685. 'wad' -> 'application/x-doom'.
  686. 'wav' -> 'audio/x-wav'.
  687. 'wax' -> 'audio/x-ms-wax'.
  688. 'wbmp' -> 'image/vnd.wap.wbmp'.
  689. 'wbxml' -> 'application/vnd.wap.wbxml'.
  690. 'wk' -> 'application/x-123'.
  691. 'wm' -> 'video/x-ms-wm'.
  692. 'wma' -> 'audio/x-ms-wma'.
  693. 'wmd' -> 'application/x-ms-wmd'.
  694. 'wml' -> 'text/vnd.wap.wml'.
  695. 'wmlc' -> 'application/vnd.wap.wmlc'.
  696. 'wmls' -> 'text/vnd.wap.wmlscript'.
  697. 'wmlsc' -> 'application/vnd.wap.wmlscriptc'.
  698. 'wmv' -> 'video/x-ms-wmv'.
  699. 'wmx' -> 'video/x-ms-wmx'.
  700. 'wmz' -> 'application/x-ms-wmz'.
  701. 'wp5' -> 'application/wordperfect5.1'.
  702. 'wpd' -> 'application/wordperfect'.
  703. 'wrl' -> 'x-world/x-vrml'.
  704. 'wsc' -> 'text/scriptlet'.
  705. 'wvx' -> 'video/x-ms-wvx'.
  706. 'wz' -> 'application/x-wingz'.
  707. 'xbm' -> 'image/x-xbitmap'.
  708. 'xcf' -> 'application/x-xcf'.
  709. 'xht' -> 'application/xhtml+xml'.
  710. 'xhtml' -> 'application/xhtml+xml'.
  711. 'xlb' -> 'application/vnd.ms-excel'.
  712. 'xls' -> 'application/vnd.ms-excel'.
  713. 'xlt' -> 'application/vnd.ms-excel'.
  714. 'xml' -> 'application/xml'.
  715. 'xpi' -> 'application/x-xpinstall'.
  716. 'xpm' -> 'image/x-xpixmap'.
  717. 'xsl' -> 'application/xml'.
  718. 'xtel' -> 'chemical/x-xtel'.
  719. 'xul' -> 'application/vnd.mozilla.xul+xml'.
  720. 'xwd' -> 'image/x-xwindowdump'.
  721. 'xyz' -> 'chemical/x-xyz'.
  722. 'zip' -> 'application/zip'.
  723. 'zmt' -> 'chemical/x-mopac-input'.
  724. '~' -> 'application/x-trash'
  725. }
  726. !
  727. defaultPort
  728. ^4000
  729. !
  730. mimeTypeFor: aString
  731. ^self mimeTypes at: (aString replace: '.*[\.]' with: '') ifAbsent: ['text/plain']
  732. !
  733. mimeTypes
  734. ^mimeTypes ifNil: [mimeTypes := self defaultMimeTypes]
  735. !
  736. printHelp
  737. console log: 'Available commandline options are:'.
  738. console log: '--help'.
  739. self commandLineSwitches do: [ :each |
  740. console log: each, ' <parameter>']
  741. !
  742. selectorForCommandLineSwitch: aSwitch
  743. "Remove the trailing '--', add ':' at the end
  744. and replace all occurences of a lowercase letter preceded by a '-' with
  745. the Uppercase letter.
  746. Example: --fallback-page becomes fallbackPage:"
  747. ^((aSwitch replace: '^--' with: '')
  748. replace: '-[a-z]' with: [ :each | each second asUppercase ]), ':'
  749. ! !
  750. !FileServer class methodsFor: 'initialization'!
  751. createServerWithArguments: options
  752. "If options are empty return a default FileServer instance.
  753. If options are given loop through them and set the passed in values
  754. on the FileServer instance.
  755. Commanline options map directly to methods in the 'accessing' protocol
  756. taking one parameter.
  757. Adding a method to this protocol makes it directly settable through
  758. command line options.
  759. "
  760. | server popFront front optionName optionValue switches |
  761. switches := self commandLineSwitches.
  762. server := self new.
  763. options ifEmpty: [^server].
  764. (options size even) ifFalse: [
  765. console log: 'Using default parameters.'.
  766. console log: 'Wrong commandline options or not enough arguments for: ' , options.
  767. console log: 'Use any of the following ones: ', switches.
  768. ^server].
  769. popFront := [:args |
  770. front := args first.
  771. args remove: front.
  772. front].
  773. [options notEmpty] whileTrue: [
  774. optionName := popFront value: options.
  775. optionValue := popFront value: options.
  776. (switches includes: optionName) ifTrue: [
  777. optionName := self selectorForCommandLineSwitch: optionName.
  778. server perform: optionName withArguments: (Array with: optionValue)]
  779. ifFalse: [
  780. console log: optionName, ' is not a valid commandline option'.
  781. console log: 'Use any of the following ones: ', switches ]].
  782. ^server.
  783. !
  784. main
  785. "Main entry point for Amber applications.
  786. Creates and starts a FileServer instance."
  787. | fileServer args |
  788. args := process argv.
  789. "Remove the first args which contain the path to the node executable and the script file."
  790. args removeFrom: 1 to: 3.
  791. args detect: [ :each |
  792. (each = '--help') ifTrue: [FileServer printHelp]]
  793. ifNone: [
  794. fileServer := FileServer createServerWithArguments: args.
  795. ^fileServer start]
  796. ! !
  797. Object subclass: #Repl
  798. instanceVariableNames: 'readline interface util session resultCount commands'
  799. package: 'AmberCli'!
  800. !Repl commentStamp!
  801. I am a class representing a REPL (Read Evaluate Print Loop) and provide a command line interface to Amber Smalltalk.
  802. On the prompt you can type Amber statements which will be evaluated after pressing <Enter>.
  803. The evaluation is comparable with executing a 'DoIt' in a workspace.
  804. My runtime requirement is a functional Node.js executable with working Readline support.!
  805. !Repl methodsFor: 'accessing'!
  806. commands
  807. ^ commands
  808. !
  809. prompt
  810. ^'amber >> '
  811. ! !
  812. !Repl methodsFor: 'actions'!
  813. clearScreen
  814. | esc cls |
  815. esc := String fromCharCode: 27.
  816. cls := esc, '[2J', esc, '[0;0f'.
  817. process stdout write: cls.
  818. interface prompt
  819. !
  820. close
  821. process stdin destroy
  822. !
  823. createInterface
  824. interface := readline createInterface: process stdin stdout: process stdout.
  825. interface on: 'line' do: [:buffer | self processLine: buffer].
  826. interface on: 'close' do: [self close].
  827. self printWelcome; setupHotkeys; setPrompt.
  828. interface prompt
  829. !
  830. eval: buffer
  831. ^ self eval: buffer on: DoIt new.
  832. !
  833. eval: buffer on: anObject
  834. | result |
  835. buffer isEmpty ifFalse: [
  836. self try: [
  837. result := Compiler new evaluateExpression: buffer on: anObject]
  838. catch: [:e |
  839. e isSmalltalkError
  840. ifTrue: [ e resignal ]
  841. ifFalse: [ process stdout write: e jsStack ]]].
  842. ^ result
  843. !
  844. printWelcome
  845. Transcript show: 'Welcome to Amber version ', Smalltalk current version, ' (NodeJS ', process versions node, ').'.
  846. Transcript show: 'Type :q to exit.'; cr.
  847. !
  848. setPrompt
  849. interface setPrompt: self prompt
  850. ! !
  851. !Repl methodsFor: 'initialization'!
  852. initialize
  853. super initialize.
  854. session := DoIt new.
  855. readline := require value: 'readline'.
  856. util := require value: 'util'.
  857. self setupCommands
  858. !
  859. setupCommands
  860. commands := Dictionary from: {
  861. {':q'} -> [process exit].
  862. {''} -> [interface prompt]}
  863. !
  864. setupHotkeys
  865. process stdin on: 'keypress' do: [:s :key | key ifNotNil: [self onKeyPress: key]].
  866. ! !
  867. !Repl methodsFor: 'private'!
  868. addVariableNamed: aString to: anObject
  869. | newClass newObject |
  870. newClass := self subclass: anObject class withVariable: aString.
  871. self encapsulateVariable: aString withValue: anObject in: newClass.
  872. newObject := newClass new.
  873. self setPreviousVariablesFor: newObject from: anObject.
  874. ^ newObject
  875. !
  876. assignNewVariable: buffer do: aBlock
  877. "Assigns a new variable and calls the given block with the variable's name and value
  878. if buffer contains an assignment expression. If it doesn't the block is called with nil for
  879. both arguments."
  880. ^ self parseAssignment: buffer do: [ :name :expr || varName value |
  881. varName := name ifNil: [self nextResultName].
  882. session := self addVariableNamed: varName to: session.
  883. [ value := self eval: varName, ' := ', (expr ifNil: [buffer]) on: session ]
  884. on: Error
  885. do: [ :e | ErrorHandler new logError: e. value := nil].
  886. aBlock value: varName value: value]
  887. !
  888. encapsulateVariable: aString withValue: anObject in: aClass
  889. "Add getter and setter for given variable to session."
  890. | compiler |
  891. compiler := Compiler new.
  892. compiler install: aString, ': anObject ^ ', aString, ' := anObject' forClass: aClass category: 'session'.
  893. compiler install: aString, ' ^ ', aString forClass: aClass category: 'session'.
  894. !
  895. executeCommand: aString
  896. "Tries to process the given string as a command. Returns true if it was a command, false if not."
  897. self commands keysAndValuesDo: [:names :cmd |
  898. (names includes: aString) ifTrue: [
  899. cmd value.
  900. ^ true]].
  901. ^ false
  902. !
  903. instanceVariableNamesFor: aClass
  904. "Yields all instance variable names for the given class, including inherited ones."
  905. ^ aClass superclass
  906. ifNotNil: [
  907. aClass instanceVariableNames copyWithAll: (self instanceVariableNamesFor: aClass superclass)]
  908. ifNil: [
  909. aClass instanceVariableNames]
  910. !
  911. isIdentifier: aString
  912. ^ aString match: '^[a-z_]\w*$' asRegexp
  913. !
  914. isVariableDefined: aString
  915. ^ (self instanceVariableNamesFor: session class) includes: aString
  916. !
  917. nextResultName
  918. resultCount := resultCount
  919. ifNotNil: [resultCount + 1]
  920. ifNil: [1].
  921. ^ 'res', resultCount asString
  922. !
  923. onKeyPress: key
  924. (key ctrl and: [key name = 'l'])
  925. ifTrue: [self clearScreen]
  926. !
  927. parseAssignment: aString do: aBlock
  928. "Assigns a new variable if the given string is an assignment expression. Calls the given block with name and value.
  929. If the string is not one no variable will be assigned and the block will be called with nil for both arguments."
  930. | assignment |
  931. assignment := (aString tokenize: ':=') collect: [:s | s trimBoth].
  932. ^ (assignment size = 2 and: [self isIdentifier: assignment first])
  933. ifTrue: [ aBlock value: assignment first value: assignment last ]
  934. ifFalse: [ aBlock value: nil value: nil ]
  935. !
  936. presentResultNamed: varName withValue: value
  937. Transcript show: varName, ': ', value class name, ' = ', value asString; cr.
  938. interface prompt
  939. !
  940. processLine: buffer
  941. "Processes lines entered through the readline interface."
  942. | show |
  943. show := [:varName :value | self presentResultNamed: varName withValue: value].
  944. (self executeCommand: buffer) ifFalse: [
  945. (self isVariableDefined: buffer)
  946. ifTrue: [show value: buffer value: (session perform: buffer)]
  947. ifFalse: [self assignNewVariable: buffer do: show]]
  948. !
  949. setPreviousVariablesFor: newObject from: oldObject
  950. (self instanceVariableNamesFor: oldObject class) do: [:each |
  951. newObject perform: each, ':' withArguments: {oldObject perform: each}].
  952. !
  953. subclass: aClass withVariable: varName
  954. "Create subclass with new variable."
  955. ^ ClassBuilder new
  956. addSubclassOf: aClass
  957. named: (self subclassNameFor: aClass) asSymbol
  958. instanceVariableNames: {varName}
  959. package: 'Compiler-Core'
  960. !
  961. subclassNameFor: aClass
  962. ^ (aClass name matchesOf: '\d+$')
  963. ifNotNil: [ | counter |
  964. counter := (aClass name matchesOf: '\d+$') first asNumber + 1.
  965. aClass name replaceRegexp: '\d+$' asRegexp with: counter asString]
  966. ifNil: [
  967. aClass name, '2'].
  968. ! !
  969. !Repl class methodsFor: 'initialization'!
  970. main
  971. self new createInterface
  972. ! !