1 globals define: #modules &builder: [ExtensibleArray new].
3 _@lobby currentModule [Module current].
4 _@lobby currentModule: m [Module current: m].
5 "TODO: form and implement a generic (non-global) idea of the current module."
7 ns@(Namespace traits) enterModule: name
13 ns@(Namespace traits) provides: objects
15 currentModule exports addAll: objects.
16 features addAll: objects
19 ns@(Namespace traits) requires: objects
21 currentModule imports addAll: objects.
24 (lobby features includes: requirement)
25 ifFalse: [warn: 'Missing feature required for loading this: ' ; requirement printString]].
28 prototypes define: #Module
29 "Modules are a collection of objects and methods, which together provide
30 certain features, and require other modules' features to function."
32 #requirements -> Set new.
33 "The set of modules that this requires before it can be loaded."
35 "Imports are objects and features referenced by the module's code."
37 "Exports are objects and features provided by the module's code."
39 "These are the non-method objects that the module defines and installs."
41 "These are the closures that the module defines and installs, whether named or
42 dispatched or anonymous."
45 m@(Module traits) initialize
46 "Reset all of the module's attributes."
48 m requirements := m requirements new.
49 m imports := m imports new.
50 m exports := m exports new.
51 m objects := m objects new.
52 m methods := m methods new.
56 m@(Module traits) new &name: name
58 m clone initialize `>> [name := name `defaultsTo: ''. ]
61 (Module traits hasSlotNamed: #current)
62 ifFalse: [Module traits addSlot: #current valued: Module new].
64 m@(Module traits) newForFileNamed: filename
66 FileModule newLocated: filename
69 m@(Module traits) copy
70 [m clone `setting: #{#requirements. #imports. #exports. #objects. #methods}
71 to: {m requirements copy.
78 m@(Module traits) newBasedOn: moduleSet
79 [m clone initialize `>>
80 [requirements := moduleSet select: #(is: Module) `er. ]].
82 m@(Module traits) provide: obj
84 "TODO: include some check here to avoid arbitrary provision."
85 m exports include: obj
88 m@(Module traits) provides: obj
90 m exports includes: obj
93 m@(Module traits) import: obj from: prereq
95 (prereq provides: obj)
96 ifTrue: [m requirements include: prereq. m imports include: obj].
100 m@(Module traits) allSelectorsSent
102 [| :result | m methods do: [| :each | result ; each allSelectorsSent]]
103 writingAs: IdentitySet
106 m@(Module traits) allSelectorsSentNotDefined
108 [| :result | m methods do:
110 result ; (each allSelectorsSent reject:
111 [| :sel | m methods anySatisfy:
112 [| :method | method selector = sel]])]]
113 writingAs: IdentitySet
116 m@(Module traits) importAll: c from: prereq
118 c do: [| :each | (prereq provides: each)
119 ifFalse: [^ (prereq error:
120 prereq name ; ' does not provide ' ; (each as: String))]].
121 m requirements include: prereq.
122 c do: [| :each | m imports include: each].
126 m@(Module traits) defines: obj
128 m defines: ((RootedPath to: obj) ifNil: [RootedPath to: obj traits])
131 m@(Module traits) defines: path@(Path traits)
134 m@(Module traits) load
135 "Loads the Module's contents, if external."
138 m@(Module traits) build
139 "Compiles the Module's contents, if external."
142 m@(Module traits) locator
145 prototypes define: #Load &parents: {Cloneable} &slots: {
147 #parser -> Syntax Parser.
148 #targetNamespace -> lobby.
150 #showMessage -> True.
152 Load traits addSlot: #Level valued: 0.
153 "How many levels of recursion has load: been invoked on."
154 "TODO: This should be a dynamic, not a global."
155 Load traits define: #DefaultSourceDir &builder: [Directory current].
156 "Records the concrete directory where the sources are available and where
157 the bootstrap occurred."
158 Load traits define: #defaultPaths &builder: [{Load DefaultSourceDir} as: ExtensibleArray].
160 l@(Load traits) of: file@(File traits)
162 l resolvePathOf: file.
164 l Level := l Level + 1.
166 [| :input | l process: input reader] &mode: file Read]
167 ensure: [l Level := oldLevel]
170 l@(Load traits) resolvePathOf: file@(File traits)
172 (file locator isSameAs: file RelativeLocator)
174 [[file exists] whileFalse:
175 [(l defaultPaths before: file locator basePath)
176 ifNil: [error: 'Could not find the file relative to any of the default directories.'. ^ Nil]
177 ifNotNilDo: [| :newL | file locator basePath: newL]]]
180 l@(Load traits) retry
183 l source close. l source reopen.
187 l@(Load traits) process: source
190 retry := ns define: #Retry &parents: {Restart}.
191 _@retry describeOn: out
192 [out ; 'Retry loading ' ; l source resource locator printString ;'\n'].
193 skip := ns define: #Skip &parents: {Restart}.
194 _@skip describeOn: out
195 [out ; 'Skip loading ' ; l source resource locator printString ; '\n' ].
197 [l run] handlingCases: {retry -> [| :_ | ^ (l retry)].
198 skip -> [| :_ | ^ Nil]}.
201 l@(Load traits) printMessage
203 l Level timesRepeat: [DebugConsole writer nextPut: $\s].
204 inform: 'Loading ' ; l source resource locator printString &target: DebugConsole.
207 l@(Load traits) skipShebang
208 "Ignore an initial she-bang line."
210 (l source peek: 2) = '#!' ifTrue: [l source upTo: $\n].
214 "Evaluate the input from the open source ReadStream."
216 l showMessage ifTrue: [l printMessage].
218 "Ensure a fresh Parser."
219 (l parser := l parser newOn: l source) do:
222 [l Level timesRepeat: [DebugConsole writer nextPut: $\s].
223 inform: 'Parsed to line ' ; l parser lexer lineNumber printString &target: DebugConsole].
224 [each evaluateIn: l targetNamespace]
225 on: RebindError do: [| :c | c return]].
228 _@(Root traits) copyright
229 "Describes the copyright for given objects/source. Override this on a per-type
230 or per-module basis."
232 (Load DefaultSourceDir / 'LICENSE') sessionDo: [| :in | in >> Console. ].
235 x@(Root traits) license
238 "Override the more primitive load: commands with high level Module hooks:"
240 ns@(Namespace traits) load: fileName@(String traits) &imports: imports
242 ns load: (fileName as: File Locator) &imports: imports
245 ns@(Namespace traits) load: locator@(File Locator traits) &in: namespace &verbose: verbose &showLoadMessage: showLoadMessage &imports: imports
246 "A command to open a file with the name, load, and compile/evaluate the
247 contents within the argument namespace or an optional override."
249 locator fileType = 'image' ifTrue:
250 [error: 'Image filename specified where Slate source expected. Make sure you run slate with the -i flag to specify an image.'].
251 src := File newNamed: locator &mode: File Read.
252 src exists ifFalse: [locator fileType `defaultsTo: 'slate'].
254 ifTrue: [ns load: src &in: namespace &verbose: verbose &showLoadMessage: showLoadMessage &imports: imports]
255 ifFalse: [warn: 'You tried to call load: on something that didn\'t describe a file. Returning the input: ' ; locator printString. locator]
258 ns@(Namespace traits) load: file@(File traits) &in: namespace
259 &verbose: verbose &showLoadMessage: showMessage &imports: imports
260 "A command to open the file, load, and compile/evaluate the
261 contents within the argument namespace or an optional override."
263 namespace `defaultsTo: ns.
264 (imports `defaultsTo: #{}) do: [| :import | namespace import: import value from: import key].
265 loader := Load clone `>>
266 [targetNamespace := namespace. verbose ?= verbose. showMessage ?= showMessage. ].
267 loader resolvePathOf: file.
268 oldLevel := loader Level.
269 loader Level := loader Level + 1.
270 [file ASCIIReader sessionDo: #(loader process: _) `er &mode: file Read]
271 ensure: [loader Level := oldLevel].
274 prototypes define: #FileModule &parents: {Module} &slots: {#source -> File}.
275 "FileModules are Modules which are drastically simplified for the purposes of
276 bootstrapping. Requirements and provisions are symbols, and other features are
279 m@(FileModule traits) newLocated: filename
280 [m new `>> [source := m source newNamed: filename &mode: File Read. ]].
282 f@(File traits) as: m@(Module traits)
283 [FileModule new `>> [source := f. ]].
285 m@(FileModule traits) locator
288 m@(FileModule traits) load &in: namespace
293 m@(FileModule traits) build
298 m@(FileModule traits) readDefinitions
299 "Reads in a file, building the module's collections while evaluating the
301 "TODO: implement it!"
304 sessionDo: [| :in | ].
308 Module traits define: #DefinitionSelectors
309 &builder: [#{#addPrototype:. #addPrototype:derivedFrom:.
310 #define:. #define:using:. #define:on:as:.
312 "Selectors for the standard accessor-producing definitions.".
314 m@(FileModule traits) parseSessionDo: block
317 [| :input | block applyWith: (Syntax Parser newOn: input reader)]
320 m@(FileModule traits) definedPaths &in: namespace
321 "Answers a Set of Path objects that would be defined if the source were
324 namespace `defaultsTo: here.
328 p do: [| :tree | tree walk:
329 [| :node accessor partPath |
330 "Handle the slot-based definitions which produce an accessor."
331 (node is: Syntax Message)
332 /\ [m DefinitionSelectors includes: node selector]
333 /\ [node arguments second is: Syntax Literal]
335 [accessor: node arguments second value.
336 partPath: node arguments first]
337 "Handle unary top-level method definitions."
339 [(node is: Syntax MethodDefinition)
340 /\ [node selector isUnarySelector]
341 /\ [node parentScope isNil]
343 [accessor := node selector.
344 partPath := node roles first]].
345 "Given an accessor, check the path for validity, then add it."
347 [(partPath as: RootedPath &root: namespace) ifNotNilDo:
348 [| :path | result nextPut: (path childNamed: accessor)]]
352 m@(FileModule traits) defines: path@(Path traits)
353 [m definedPaths includes: path].
355 globals ensureNamespace: #AutoLoader
356 "Manages 'autoloading' stubs - these are mock accessors for unloaded features
357 which load those features transparently and return them once done."
358 &slots: {#defs -> Dictionary new.
359 #storage -> (File newNamed: 'AutoLoad').
361 "Extra generic libraries."
362 ({'extlib'. 'time'. 'dimensioned'. 'predicate'. 'group'. 'heap'. 'skiplist'.
363 'tree'. 'tokenizer'. 'sortedSet'. 'relation'.
364 'singleSet'. 'matrix'. 'wordarray'.
365 'terminal'. 'version'. 'test'. 'random'. 'xml'.
366 'suspension'. 'process'.
368 collect: [| :name | 'src/lib/' ; name ; '.slate']) ;
369 "Networking libraries, platform libraries."
370 {'src/net/sockets.slate'. 'src/net/http.slate'} ;
371 "Bootstrapper libraries."
372 ({'bootstrap'. 'build'}
373 collect: [| :name | 'src/mobius/' ; name ; '.slate']) as: Set)}.
375 l@AutoLoader readFromSourceIn: file
376 "Parses the source in the specified file and creates entries for each path
379 mod := FileModule newLocated: file.
380 (paths := mod definedPaths) do:
382 path isOneSlotFromDefinition /\ [(paths includes: path parent) not]
383 ifTrue: [l defs at: path := mod]].
386 l@AutoLoader performScan &files: fileList
387 "Scans the source in the given collection of filenames or the default, adding
388 to the in-memory database."
390 fileList `defaultsTo: l standardFiles.
391 fileList do: [| :file |
392 inform: 'Scanning \'' ; file ; '\''.
393 l readFromSourceIn: file]
396 l@AutoLoader install: m@(Module traits) when: selector isCalledOn: namespace
397 "The basic unit of creating an auto-loading method. This checks for a pre-
398 existing method on the exact same object to avoid duplicate work, and informs
399 the user of the creation. The method itself tells the module to load itself,
400 which will presumably clobber that method called, so that the final action
401 of re-calling it will return the intended object."
403 (selector isFoundOn: {namespace}) ifTrue: [^ Nil].
404 inform: 'Installing auto-loader for: ' ; selector printString
405 ; ' in: ' ; ((RootedPath from: here to: namespace) as: Syntax Node) printString.
407 selector removeMethodFrom: {namespace}. "Remove this method before replacement."
408 m load. "Have the module object load itself, presumably replacing this."
409 l installReadyItems. "Have AutoLoader rescan for further hooks."
410 "Re-call the method, presuming that the module installed a replacement."
411 [selector sendTo: {ns}] on: MethodNotFound do:
412 [| :c | error: 'The module failed to define this accessor.']]
413 asMethod: selector on: {namespace}
416 l@AutoLoader installReadyItems
417 "Iterate through the in-memory definitions, installing methods for those
418 that are one slot definition away from completion."
420 l defs keysAndValuesDo:
422 path isOneSlotFromDefinition
424 [l install: mod when: path names last isCalledOn: path parent target]]
427 l@AutoLoader readStorage &from: file &in: namespace
428 "Reads a quirky file format we've defined from path-spec -> locator-spec per
429 line in the text file or default storage."
431 file `defaultsTo: l storage.
432 namespace `defaultsTo: here.
434 ifFalse: [warn: 'The database file for auto-loaders is not present.'.
439 [r isAtEnd] whileFalse:
442 (line indexOfSubSeq: ' ->') ifNotNilDo:
443 [| :splitIndex path filename |
444 path := (line first: splitIndex + 1) split
445 as: RootedPath &root: namespace.
446 filename := String readFrom: (line copyFrom: splitIndex + 4).
447 AutoLoader defs at: path := FileModule newLocated: filename]]]].
451 l@AutoLoader writeToStorage
452 "Writes definitions to the default storage."
457 l defs keysAndValuesDo:
459 out ; ((path as: Syntax Node) printString
460 ; ' -> ' ; (mod locator as: String) printString as: ByteArray) ; '\n']]
461 &mode: l storage CreateWrite.
464 lobby ensureNamespace: #systems.
466 prototypes define: #System &slots: {#modules -> Set new}.
468 s@(System traits) newForAll: modules
469 "Creates a new System object for the given Module objects."
470 [(s clone `setting: #{#modules} to: {modules as: s modules}) `>>
471 [| :newS | modules do: [| :each | newS fillRequirementsFor: each]. ]
474 s@(System traits) fillRequirementsFor: module
475 "Fills the requirements slot of the module, based on the knowledge that the
476 System has by holding several other Modules, and return the requirements."
477 [| features covered |
478 features := module imports.
479 covered := Set newSizeOf: features.
481 do: [| :each coverage |
482 coverage := each exports intersection: features.
484 ifFalse: [module requirements include: each.
485 covered includeAll: coverage]].
489 s@(System traits) add: module
491 s modules include: module.
492 s fillRequirementsFor: module.
496 s@(System traits) build
497 "Compile the System's modules."
499 s modules do: #build`er.
502 s@(System traits) load
503 "Load the System's modules in a consistent order."
505 remaining := s modules copy.
510 (features includesAllOf: each requirements)
511 ifTrue: [each load. remaining remove: each]]].
514 s@(System traits) load: module
515 "Loads the given module and recursively re-loads modules that depend upon it."
519 do: [| :each | (each dependsOn: module) ifTrue: [s load: each]].
522 systems define: #Core &builder: [System newForAll: globals modules].
523 "This relies on being loaded just after all the standard libraries, and more
524 importantly before other systems."