aboutsummaryrefslogtreecommitdiff
path: root/src/interp/sys-utility.boot
blob: c22a2d6fe02b36c1ed5735e7dd0eb4aa984f084b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
-- Copyright (C) 2007-2013 Gabriel Dos Reis.
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
--     - Redistributions of source code must retain the above copyright
--       notice, this list of conditions and the following disclaimer.
--
--     - Redistributions in binary form must reproduce the above copyright
--       notice, this list of conditions and the following disclaimer in
--       the documentation and/or other materials provided with the
--       distribution.
--
--     - Neither the name of The Numerical Algorithms Group Ltd. nor the
--       names of its contributors may be used to endorse or promote products
--       derived from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--

-- This file defines some utility functions common to both the compiler
-- and interpreter.

import sys_-constants
import vmlisp
import hash
namespace BOOT

module sys_-utility where
  probeReadableFile : %String -> %Maybe %String
  remove!: (%List %Thing,%Thing) -> %List %Thing
  displayTextFile: %Thing -> %Void
  upwardCut: (%Thing, %List %Thing) -> %List %Thing
  symbolPosition: (%Symbol,%List %Symbol) -> %Maybe %Short
  valuePosition: (%Thing,%List %Thing) -> %Maybe %Short
  symbolTarget: (%Symbol,%List %Thing) -> %Maybe %Thing
  scalarAssoc: (%Thing,%List %Thing) -> %Maybe %Pair(%Thing,%Thing)
  scalarTarget: (%Thing,%List %Thing) -> %Maybe %Thing

--%
$COMBLOCKLIST := nil

++ Constants describing byte order
%littleEndian == KEYWORD::%littleEndian
%bigEndian == KEYWORD::%bigEndian
%unknownEndian == KEYWORD::%unknownEndian

++ The byte order of the host machine running OpenAxiom.
%hostByteOrder() ==
  getHostByteOrder() = 1 => %littleEndian
  getHostByteOrder() = 2 => %bigEndian
  %unknownEndian

--%

++ getVMType returns an approximation of the underlying object type
++ representation of a domain, as a Lisp type specifier as seen by
++ the runtime system.
getVMType d ==
  ident? d => 
    d is "*" => d
    "%Thing"
  string? d => "%Thing"            -- literal flag parameter
  case (d' := devaluate d) of
    Void => "%Void"
    Identifier => "%Symbol"
    Boolean => "%Boolean"
    Byte => "%Byte"
    Character => "%Char"
    SingleInteger => "%Short"
    Integer => "%Integer"
    NonNegativeInteger => ["%IntegerSection",0]
    PositiveInteger => ["%IntegerSection",1]
    IntegerMod => "%Integer"
    DoubleFloat => "%DoubleFloat"
    String => "%String"
    List => ["%List",getVMType second d']
    Vector => ["%Vector",getVMType second d']
    PrimitiveArray => ["%SimpleArray", getVMType second d']
    Pair => ["%Pair",getVMType second d',getVMType third d']
    Union => ["%Pair",'%Short,'%Thing]
    Record =>
      #rest d' > 2 => "%Shell"
      ["%Pair",'%Thing,'%Thing]
    IndexedList => ["%List", getVMType second d']
    Int8 => ["SIGNED-BYTE", 8]
    Int16 => ["SIGNED-BYTE", 16]
    Int32 => ["SIGNED-BYTE", 32]
    UInt8 => ["UNSIGNED-BYTE", 8]
    UInt16 => ["UNSIGNED-BYTE", 16]
    UInt32 => ["UNSIGNED-BYTE", 32]
    otherwise => "%Thing"                 -- good enough, for now.

--%

++ returns true if `f' is bound to a macro.
macrop: %Thing -> %Boolean
macrop f ==
  ident? f and not null MACRO_-FUNCTION f

++ returns true if `f' is bound to a function
functionp: %Thing -> %Boolean
functionp f ==
  ident? f => functionSymbol? f and null MACRO_-FUNCTION f
  function? f

++ returns true if `x' is contained in `y'.
CONTAINED: (%Thing,%Thing) -> %Boolean
CONTAINED(x,y) == main where
  main() ==
    symbol? x => eq(x,y)
    equal(x,y)
  eq(x,y) ==
    cons? y => eq(x, first y) or eq(x, rest y)
    symbolEq?(x,y)
  equal(x,y) ==
    y isnt [.,:.] => x = y
    equal(x, first y) or equal(x, rest y)

++ Returns all the keys of association list `x'
-- ??? Should not this be named `alistAllKeys'?
ASSOCLEFT: %Thing -> %Thing
ASSOCLEFT x ==
  x isnt [.,:.] => x
  [first p for p in x]

++ Returns all the datums of association list `x'.
-- ??? Should not this be named `alistAllValues'?
ASSOCRIGHT: %Thing -> %Thing
ASSOCRIGHT x ==
  x isnt [.,:.] => x
  [rest p for p in x]

++ Put the association list pair `(x . y)' into `l', erasing any 
++ previous association for `x'.
ADDASSOC: (%Thing,%Thing,%Alist(%Thing,%Thing)) -> %Alist(%Thing,%Thing)
ADDASSOC(x,y,l) ==
  l isnt [.,:.] => [[x,:y],:l]
  x = first first l => [[x,:y],:rest l]
  [first l,:ADDASSOC(x,y,rest l)]

++ Remove any assocation pair `(u . x)' from list `v'.
DELLASOS: (%Thing,%Alist(%Thing,%Thing)) -> %Alist(%Thing,%Thing)
DELLASOS(u,v) ==
  v isnt [.,:.] => nil
  u = first first v => rest v
  [first v,:DELLASOS(u,rest v)]


++ Return the datum associated with key `x' in association list `y'.
-- ??? Should not this be named `alistValue'?
LASSOC: (%Thing,%Alist(%Thing,%Thing)) -> %Thing
LASSOC(x,y) ==
  y isnt [.,:.] => nil
  x = first first y => rest first y
  LASSOC(x,rest y)

++ Return the key associated with datum `x' in association list `y'.
rassoc: (%Thing,%Alist(%Thing,%Thing)) -> %Thing
rassoc(x,y) ==
  y isnt [.,:.] => nil
  x = rest first y => first first y
  rassoc(x,rest y)

++ Reclaim unreachable objects.
RECLAIM() ==
)if %hasFeature KEYWORD::GCL
  SI::GBC true
)elseif %hasFeature KEYWORD::SBCL
  SB_-EXT::GC()
)elseif %hasFeature KEYWORD::CLISP
  EXT::GC()
)else
  nil
)endif

++
makeAbsoluteFilename: %String -> %String
makeAbsoluteFilename name ==
  strconc(systemRootDirectory(),name)

++ returns true if `file' exists as a pathname.
existingFile? file ==
  PROBE_-FILE file => true
  false

probeReadableFile file ==
  readablep file > 0 => file
  nil

++
$REPLACE(filespec1,filespec2) ==
  filespec1 := makeFullFilePath filespec1
  removeFile filespec1
  renameFile(makeFullFilePath filespec2, filespec1)

++
checkMkdir path ==
  mkdir path = 0 => true
  systemError ['"cannot create directory",:bright path]

++ return the pathname to the system module designated by `m'.
getSystemModulePath m ==
  d := systemAlgebraDirectory() => strconc(d,m,'".",$faslType)
  strconc(systemRootDirectory(),'"algebra/",m,'".",$faslType)

++ load module in `path' that supposedly will define the function 
++ indicated by `name'.
loadModule: (%String,%Symbol) -> %Thing
loadModule(path,name) ==
  FMAKUNBOUND name
  LOAD path

--% numerics
log10 x ==
  LOG(x,10)

bitand: (%Short,%Short) -> %Short
bitand(x,y) ==
  BOOLE(BOOLE_-AND,x,y)

bitior: (%Short,%Short) -> %Short
bitior(x,y) ==
  BOOLE(BOOLE_-IOR,x,y)

--% Hash table

hashTable cmp ==
  testFun :=
    cmp in '(ID EQ) => function sameObject?
    cmp = 'EQL => function scalarEq?
    cmp = 'EQUAL => function EQUAL
    error '"bad arg to hashTable"
  makeTable testFun

--% Trees to Graphs

minimalise x ==
  min(x,hashTable 'EQUAL) where
    min(x,ht) ==
      y := tableValue(ht,x)
      y => y
      cons? x =>
        z := min(first x,ht)
        if not sameObject?(z,first x) then x.first := z
        z := min(rest x,ht)
        if not sameObject?(z,rest x) then x.rest := z
        hashCheck(x,ht)
      vector? x =>
        for i in 0..maxIndex x repeat
          x.i := min(x.i,ht)
        hashCheck(x,ht)
      string? x => hashCheck(x,ht)
      x
    hashCheck(x,ht) ==
      y := tableValue(ht,x)
      y => y
      tableValue(ht,x) := x
      x

--% File IO
$InputIOMode == KEYWORD::INPUT
$OutputIOMode == KEYWORD::OUTPUT
$BothWaysIOMode == KEYWORD::IO
$ClosedIOMode == KEYWORD::CLOSED

++ return a binary stream open for `file' in mode `mode'; nil
++ if something went wrong.  This function is used by the Algebra.
openBinaryFile(file,mode) ==
  mode = $InputIOMode =>
    OPEN(file,direction <- mode,
      if_-does_-not_-exist <- nil,element_-type <- "%Byte")
  OPEN(file,direction <- mode,
    if_-exists <- KEYWORD::SUPERSEDE,element_-type <- "%Byte")

++ Write byte `b' to output binary file `ofile'.
writeByteToFile(ofile,b) ==
  writeByte(b,ofile)

--%
stringImage x ==
  symbol? x => symbolName x
  string? x => strconc('"_"",x,'"_"")
  toString x

--% Socket I/O

++ Attempt to establish a client TCP/IP socket connection.  The IP numeric
++ address is specified by the first argument; second argument is the
++ version of IP used (4 or 6); third argument is the desired port.
++ Return %nothing on failure, otherwise the file descriptor corresponding
++ to the obtained client socket.
connectToHostAndPort(addr,prot,port) ==
  (socket := doConnectToHostAndPort(addr,prot,port)) < 0 => %nothing
  socket

++ Attempt to read a byte from the socket `s'.  If unsuccessful,
++ return %nothing.
readByteFromStreamSocket s ==
  (byte := doReadByteFromStreamSocket s) < 0 => %nothing
  COERCE(byte,"%Byte")

writeByteToStreamSocket(s,b) ==
  (byte := doWriteByteToStreamSocket(s,b)) < 0 => %nothing
  COERCE(byte,"%Byte")

--%
makeByteBuffer(n,b == 0) ==
  newVector(n,element_-type <- "%Byte",initial_-element <- b)

++ Return the position of the symbol `s' in the list `l', if present.
++ Otherwise return nil.
symbolPosition(s,l) ==
  or/[i for i in 0.. for x in l | symbolEq?(s,x)]

valuePosition(s,l) ==
  or/[i for i in 0.. for x in l | valueEq?(s,x)]

--% assoc

scalarAssoc(c,l) ==
  or/[scalarEq?(c,first x) and leave x for x in l | cons? x] or nil

stringAssoc(s,l) ==
  or/[stringEq?(s,first x) and leave x for x in l | cons? x] or nil

--% lassoc

symbolTarget(s,l) ==
  p := symbolAssoc(s,l) => rest p
  nil

scalarTarget(s,l) ==
  p := scalarAssoc(s,l) => rest p
  nil

--%
remove!(l,x) ==
  l = nil => nil
  valueEq?(first l,x) => rest l
  p := l
  repeat
    p isnt [.,.,:.] => return l
    valueEq?(second p,x) =>
      p.rest := p.rest.rest
      return l
    p := rest p
      
sortBy(k,l) ==
  SORT(copyList l,function GGREATERP,key <- k)

++ Return the list of objects that follow x in l, including x itself.
++ Otherwise return nil.
upwardCut(x,l) ==
  repeat
    l isnt [.,:.] => return nil
    sameObject?(x,first l) => return l
    l := rest l

--%
displayTextFile f ==
  try
    stream := inputTextFile f
    while (line := readLine stream) ~= %nothing repeat
      writeLine(line,$OutputStream)
  finally
    stream ~= nil => closeStream stream

--%
macro loopBody x ==
  take(-2,x).first

--%
macro constructorDB ctor ==
  property(ctor,'DATABASE)
  
--%
structure %Libstream ==
  Record(mode: %IOMode, dir: %Pathname,tbl: %Thing, idxst: %Stream,
    cdst: %Stream,insnst: %Stream,tmpdir: %Pathname) with
      libIOMode == (.mode)
      libDirname == (.dir)
      libIndexTable == (.tbl)
      libIndexStream == (.idxst)
      libCodeStream == (.cdst)
      libInsnStream == (.insnst)
      libStationaryDirname == (.tmpdir)

makeLibstream(m,p,idx==nil,st==nil) ==
  mk%Libstream(m,p,idx,st,nil,nil,nil)

makeFilename(filearg,filetype==nil) ==
  if ident? filetype then
    filetype := symbolName filetype
  filePath? filearg => filePathString
    filePathType filearg ~= nil => filearg
    makeFilePath(directory <- filePathDirectory filearg,
      name <- filePathName filearg, type <- filetype)
  string? filearg and filePathType filearg ~= nil and filetype = nil => filearg
  string? filearg and string? filetype and filePathType filearg ~= nil
    and stringEq?(filePathType filearg,filetype) => filearg
  filearg is [.,:.] =>
    makeFilename(first filearg,second filearg or filetype)
  if string? filetype then
    filetype := makeSymbol filetype
  ft := rest symbolAssoc(filetype,$FILETYPE_-TABLE) or filetype
  ft = nil =>
    string? filearg => filearg
    ident? filearg => symbolName filearg
    toString filearg
  strconc(toString filearg,'".",toString ft)

makeFullFilePath(filearg,filetype==nil) ==
  filePathString mergeFilePaths makeFilename(filearg,filetype)
  
getDirectoryList ft ==
  here := getWorkingDirectory()
  ft in '("NRLIB" "DAASE" "EXPOSED") =>
    $UserLevel = 'development => [here,:$LIBRARY_-DIRECTORY_-LIST]
    $LIBRARY_-DIRECTORY_-LIST
  home := filePathString userHomeDirectory()
  dirs := 
    stringMember?(home,$DIRECTORY_-LIST) => $DIRECTORY_-LIST
    [home,:$DIRECTORY_-LIST]
  stringMember?(here,dirs) => dirs
  [here,:dirs]

makeInputFilename(filearg,filetype == nil) ==
  filename := makeFilename(filearg,filetype)
  dirname := filePathDirectory filename
  dirname = nil or dirname is [KEYWORD::RELATIVE,:.] =>
    or/[probeReadableFile strconc(dir,filename)
         for dir in getDirectoryList filePathType filename]
           or probeReadableFile filename
  probeReadableFile filename

findFile(spec,exts) ==
  name :=
    spec is [.,:.] => first spec
    spec
  if spec is [.,:.] then
    exts := [second spec,:exts]
  or/[makeInputFilename(name,ext) for ext in exts]