Revert "Revert "Made use of ::= in core libraries and defined a RebindError condition...
[cslatevm.git] / src / lib / module.slate
blob1c5fee6af2749fbd6e4e66cd178edf289b40fc65
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
9   modules include: name.
10   currentModule: 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.
22   objects do:
23     [| :requirement |
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."
31   &slots: {
32 #requirements -> Set new.
33 "The set of modules that this requires before it can be loaded."
34 #imports -> Set new.
35 "Imports are objects and features referenced by the module's code."
36 #exports -> Set new.
37 "Exports are objects and features provided by the module's code."
38 #objects -> Set new.
39 "These are the non-method objects that the module defines and installs."
40 #methods -> Set new.
41 "These are the closures that the module defines and installs, whether named or
42 dispatched or anonymous."
43 #name -> ''}.
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.
53   m
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.
72         m imports copy.
73         m exports copy.
74         m objects copy.
75         m methods 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].
97   m
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:
109     [| :each |
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].
123   m
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)
132 [overrideThis].
134 m@(Module traits) load
135 "Loads the Module's contents, if external."
136 [overrideThis].
138 m@(Module traits) build
139 "Compiles the Module's contents, if external."
140 [overrideThis].
142 m@(Module traits) locator
143 [overrideThis].
145 prototypes define: #Load &parents: {Cloneable} &slots: {
146   #source.
147   #parser -> Syntax Parser.
148   #targetNamespace -> lobby.
149   #verbose -> False.
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)
161 [| oldLevel |
162   l resolvePathOf: file.
163   oldLevel := l Level.
164   l Level := l Level + 1.
165   [file sessionDo:
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)
173     ifTrue:
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
182   l source reset.
183   l source close. l source reopen.
184   l process: l source.
187 l@(Load traits) process: source
188 [| retry skip ns |
189   ns := Namespace new. 
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' ].
196   l source := source.
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].
213 l@(Load traits) run
214 "Evaluate the input from the open source ReadStream."
216   l showMessage ifTrue: [l printMessage].
217   l skipShebang.
218   "Ensure a fresh Parser."
219   (l parser := l parser newOn: l source) do:
220     [| :each |
221      l verbose ifTrue:
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
236 [x copyright].
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."
248 [| src |
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'].
253   src exists
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."
262 [| oldLevel loader |
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
277 ignored."
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
286 [m source locator].
288 m@(FileModule traits) load &in: namespace
290   Load of: m source
293 m@(FileModule traits) build
295   m load
298 m@(FileModule traits) readDefinitions
299 "Reads in a file, building the module's collections while evaluating the
300 contents."
301 "TODO: implement it!"
303   m source
304     sessionDo: [| :in | ].
305   m
308 Module traits define: #DefinitionSelectors
309               &builder: [#{#addPrototype:. #addPrototype:derivedFrom:.
310                          #define:. #define:using:. #define:on:as:.
311                          #ensureNamespace:}]
312 "Selectors for the standard accessor-producing definitions.".
314 m@(FileModule traits) parseSessionDo: block
316   m source sessionDo:
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
322 evaluated."
324   namespace `defaultsTo: here.
325   [| :result |
326    m parseSessionDo:
327      [| :p |
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]
334            ifTrue:
335              [accessor: node arguments second value.
336               partPath: node arguments first]
337            "Handle unary top-level method definitions."
338            ifFalse:
339              [(node is: Syntax MethodDefinition)
340                 /\ [node selector isUnarySelector]
341                 /\ [node parentScope isNil]
342                 ifTrue:
343                   [accessor := node selector.
344                    partPath := node roles first]].
345          "Given an accessor, check the path for validity, then add it."
346          accessor ifNotNil:
347            [(partPath as: RootedPath &root: namespace) ifNotNilDo:
348              [| :path | result nextPut: (path childNamed: accessor)]]
349          ]]]] writingAs: Set
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').
360            #standardFiles -> (
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'.
367     'graph'. 'digraph'}
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
377 directly defined."
378 [| mod paths |
379   mod := FileModule newLocated: file.
380   (paths := mod definedPaths) do:
381     [| :path |
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.
406   [| :ns |
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:
421     [| :path :mod |
422      path isOneSlotFromDefinition
423        ifTrue:
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.
433   file exists
434     ifFalse: [warn: 'The database file for auto-loaders is not present.'.
435               ^ Nil].
436   file sessionDo:
437     [| :f r |
438      r := f reader lines.
439      [r isAtEnd] whileFalse:
440        [r next ifNotNilDo:
441          [| :line |
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]]]].
448   l installReadyItems
451 l@AutoLoader writeToStorage
452 "Writes definitions to the default storage."
454   l storage sessionDo:
455     [| :f out |
456      out := f writer.
457      l defs keysAndValuesDo:
458        [| :path :mod |
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.
480   s modules
481     do: [| :each coverage |
482          coverage := each exports intersection: features.
483          coverage isEmpty
484            ifFalse: [module requirements include: each.
485                      covered includeAll: coverage]].
486   module requirements
489 s@(System traits) add: module
491   s modules include: module.
492   s fillRequirementsFor: module.
493   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."
504 [| remaining |
505   remaining := s modules copy.
506   [remaining isEmpty]
507     whileFalse:
508       [remaining do:
509         [| :each |
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."
517   module load.
518   s modules
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."