modernc.org/knuth@v0.0.4/runtime.go (about) 1 // Copyright 2023 The Knuth Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package knuth // modernc.org/knuth 6 7 import ( 8 "fmt" 9 "io" 10 "math" 11 "os" 12 "strings" 13 "unsafe" 14 ) 15 16 var ( 17 _ File = (*binaryFile)(nil) 18 _ File = (*textFile)(nil) 19 _ File = (*poolFile)(nil) 20 _ error = Error("") 21 ) 22 23 // Error is a specific error implementation. 24 type Error string 25 26 func (e Error) Error() string { return string(e) } 27 28 // WriteWidth is the type of width in `writeln(foo: width);`. 29 type WriteWidth int 30 31 // File represents a Pascal file. 32 type File interface { 33 ByteP() *byte 34 Close() 35 CurPos() int32 36 Data4P() *[4]byte 37 EOF() bool 38 EOLN() bool 39 ErStat() int32 40 Get() 41 Put() 42 Read(args ...interface{}) 43 Readln(args ...interface{}) 44 Reset(args ...interface{}) 45 Rewrite(args ...interface{}) 46 SetPos(int32) 47 Write(args ...interface{}) 48 Writeln(args ...interface{}) 49 } 50 51 type file struct { 52 buf []byte 53 name string 54 r io.Reader 55 r0 io.Reader 56 reset func(string) (io.Reader, error) 57 w io.Writer 58 w0 io.Writer 59 60 atEOF bool 61 atEOLN bool 62 erStat int32 63 isText bool 64 65 dbgOff int //TODO- 66 } 67 68 func (f *file) ByteP() *byte { 69 f.boot() 70 return &f.buf[0] 71 } 72 73 func (f *file) Data4P() *[4]byte { 74 f.boot() 75 return (*[4]byte)(unsafe.Pointer(&f.buf[0])) 76 } 77 78 func (f *file) boot() { 79 if len(f.buf) == 0 { 80 f.buf = f.buf[:cap(f.buf)] 81 f.Get() 82 } 83 } 84 85 func (f *file) Put() { 86 panic(todo("")) 87 } 88 89 func (f *file) EOLN() bool { 90 if !f.isText { 91 panic(todo("")) 92 } 93 94 f.boot() 95 return f.atEOLN || f.atEOF 96 } 97 98 func (f *file) ErStat() int32 { 99 return f.erStat 100 } 101 102 func (f *file) Close() { 103 if f.r0 != nil { 104 if x, ok := f.r0.(io.Closer); ok { 105 x.Close() 106 } 107 f.r0 = nil 108 } 109 if f.r != nil { 110 f.erStat = 0 111 if x, ok := f.r.(io.Closer); ok { 112 if err := x.Close(); err != nil { 113 f.erStat = 1 114 } 115 } 116 f.r = nil 117 } 118 if f.w0 != nil { 119 if x, ok := f.w.(io.Closer); ok { 120 x.Close() 121 } 122 f.w0 = nil 123 } 124 if f.w != nil { 125 f.erStat = 0 126 if x, ok := f.w.(io.Closer); ok { 127 if err := x.Close(); err != nil { 128 f.erStat = 1 129 } 130 } 131 f.w = nil 132 } 133 } 134 135 func (f *file) EOF() bool { 136 // trc("EOF %q: %v", f.name, f.atEOF) 137 f.boot() 138 return f.atEOF 139 } 140 141 func (f *file) Read(args ...interface{}) { 142 // trc("READ %p %q (%v:)", f, f.name, origin(2)) 143 if f.r == nil && f.r0 != nil { 144 f.r = f.r0 145 f.r0 = nil 146 } 147 f.boot() 148 switch len(f.buf) { 149 case 1: 150 for _, v := range args { 151 switch x := v.(type) { 152 case *byte: 153 *x = f.buf[0] 154 default: 155 panic(todo("%T", x)) 156 } 157 f.Get() 158 } 159 default: 160 panic(todo("", len(f.buf))) 161 } 162 } 163 164 func (f *file) Get() { 165 if f.atEOF { 166 panic(todo("")) 167 } 168 169 f.atEOLN = false 170 if c, err := f.r.Read(f.buf[:]); c != len(f.buf) { 171 if err != io.EOF { 172 panic(todo("")) 173 } 174 175 f.atEOLN = true 176 f.atEOF = true 177 // trc("RD EOF %q", f.name) 178 return 179 } 180 181 // trc("RD %#04x |% x| %q", f.dbgOff, f.buf, f.name) 182 f.dbgOff += len(f.buf) 183 if f.isText && f.buf[0] == '\n' { 184 f.atEOLN = true 185 f.buf[0] = ' ' 186 } 187 } 188 189 func (f *file) Readln(args ...interface{}) { 190 f.Read(args...) 191 for !f.EOLN() { 192 f.Get() 193 } 194 f.Get() 195 } 196 197 func (f *file) Reset(args ...interface{}) { 198 f.dbgOff = 0 //TODO- 199 if debug { 200 defer func() { 201 trc("RESET %p %q %v -> erStat %v (%v: %v:)", f, f.name, args, f.erStat, origin(3), origin(4)) 202 }() 203 } 204 f.atEOF = false 205 f.atEOLN = false 206 f.buf = f.buf[:0] 207 switch len(args) { 208 case 0: 209 if f.r == nil && f.r0 != nil { 210 f.r = f.r0 211 f.r0 = nil 212 // trc("using f.r0") 213 break 214 } 215 216 switch x, ok := f.r.(io.Seeker); { 217 case ok: 218 if _, err := x.Seek(0, io.SeekStart); err != nil { 219 panic(todo("")) 220 } 221 default: 222 panic(todo("")) 223 } 224 225 case 1: 226 switch x := args[0].(type) { 227 case string: 228 f.open(strings.TrimRight(x, " ")) 229 default: 230 panic(todo("%T", x)) 231 } 232 case 2: 233 switch x := args[0].(type) { 234 case string: 235 switch y := args[1].(type) { 236 case string: 237 switch { 238 case x == "TTY:" && y == "/O/I" && f.r0 != nil: 239 f.name = x + y 240 f.r = f.r0 241 f.r0 = nil 242 case y == "/O": 243 f.open(strings.TrimRight(x, " ")) 244 default: 245 panic(todo("%q %q %v", x, y, f.w != nil)) 246 } 247 default: 248 panic(todo("%T", y)) 249 } 250 default: 251 panic(todo("%T", x)) 252 } 253 default: 254 panic(todo("", args, len(args))) 255 } 256 } 257 258 func (f *file) open(name string) { 259 if f.reset != nil { 260 if x, ok := f.r.(io.Closer); ok { 261 x.Close() 262 f.r = nil 263 } 264 f.name = name 265 f.atEOF = false 266 f.atEOLN = false 267 f.erStat = 0 268 var err error 269 f.r, err = f.reset(name) 270 if err == nil { 271 // trc("OPEN %q ok", f.name) 272 return 273 } 274 275 // trc("OPEN %q FAIL", f.name) 276 f.r = nil 277 f.erStat = 1 278 return 279 280 } 281 282 f.name = name 283 f.atEOF = false 284 f.atEOLN = false 285 f.erStat = 0 286 var err error 287 f.r, err = os.Open(name) 288 if err == nil { 289 // trc("OPEN %q ok", f.name) 290 return 291 } 292 293 // trc("OPEN %q FAIL", f.name) 294 // wd, _ := os.Getwd() 295 // m, _ := filepath.Glob("*") 296 // trc("in %q: %q", wd, m) 297 f.r = nil 298 f.erStat = 1 299 } 300 301 func (f *file) Rewrite(args ...interface{}) { 302 f.dbgOff = 0 //TODO- 303 if debug { 304 defer func() { 305 trc("REWRITE %p %q %v -> erStat %v ()", f, f.name, args, f.erStat) 306 }() 307 } 308 f.atEOF = true 309 f.atEOLN = false 310 switch len(args) { 311 case 0: 312 if f.w == nil && f.w0 != nil { 313 f.w = f.w0 314 f.w0 = nil 315 // trc("using f.w0") 316 break 317 } 318 319 panic(todo("")) 320 case 2: 321 switch x := args[0].(type) { 322 case string: 323 switch y := args[1].(type) { 324 case string: 325 switch { 326 case x == "TTY:" && y == "/O" && f.w0 != nil: 327 f.name = x + y 328 f.w = f.w0 329 f.w0 = nil 330 case y == "/O": 331 f.erStat = 0 332 if f.w != nil { 333 panic(todo("")) 334 } 335 336 var err error 337 f.name = strings.TrimRight(x, " ") 338 if f.w, err = os.Create(f.name); err != nil { 339 f.w = nil 340 f.erStat = 1 341 break 342 } 343 344 // trc("CREATE %q ok", f.name) 345 default: 346 panic(todo("%q %q", x, y)) 347 } 348 default: 349 panic(todo("%T", y)) 350 } 351 default: 352 panic(todo("%T", x)) 353 } 354 default: 355 panic(todo("", args, len(args))) 356 } 357 } 358 359 func (f *file) CurPos() int32 { 360 switch { 361 case f.r != nil: 362 s, ok := f.r.(io.ReadSeeker) 363 if !ok { 364 panic(todo("")) 365 } 366 367 n, err := s.Seek(0, io.SeekCurrent) 368 if err != nil || n > math.MaxInt32 { 369 panic(todo("")) 370 } 371 372 return int32(n) 373 case f.w != nil: 374 panic(todo("")) 375 default: 376 panic(todo("")) 377 } 378 } 379 380 func (f *file) SetPos(n int32) { 381 switch { 382 case f.r != nil: 383 s, ok := f.r.(io.ReadSeeker) 384 if !ok { 385 panic(todo("")) 386 } 387 388 switch { 389 case n < 0: 390 if _, err := s.Seek(0, io.SeekEnd); err != nil { 391 panic(todo("")) 392 } 393 394 f.atEOF = true 395 default: 396 if _, err := s.Seek(int64(n), io.SeekStart); err != nil { 397 panic(todo("")) 398 } 399 400 f.atEOF = false 401 f.atEOLN = false 402 f.Get() 403 } 404 case f.w != nil: 405 panic(todo("")) 406 default: 407 panic(todo("")) 408 } 409 } 410 411 type textFile struct { 412 *file 413 } 414 415 // NewTextFile returns File suitable for Pascal file type 'text'. 416 func NewTextFile(r io.Reader, w io.Writer, open func(string) (io.Reader, error)) File { 417 return &textFile{ 418 &file{ 419 buf: make([]byte, 1), 420 isText: true, 421 r0: r, 422 reset: open, 423 w0: w, 424 }, 425 } 426 } 427 428 func (f *textFile) Write(args ...interface{}) { 429 if f.w == nil && f.w0 != nil { 430 f.w = f.w0 431 f.w0 = nil 432 // trc("used f.w0") 433 } 434 var a [][]interface{} 435 for i := 0; i < len(args); i++ { 436 switch x := args[i].(type) { 437 case WriteWidth: 438 a[len(a)-1] = append(a[len(a)-1], int(x)) 439 default: 440 a = append(a, []interface{}{x}) 441 } 442 } 443 for _, v := range a { 444 switch x := v[0].(type) { 445 case string: 446 switch len(v) { 447 case 1: 448 if _, err := fmt.Fprintf(f.w, "%s", x); err != nil { 449 panic(todo("", err)) 450 } 451 case 2: 452 if _, err := fmt.Fprintf(f.w, "%*s", v[1], v[0]); err != nil { 453 panic(todo("", err)) 454 } 455 default: 456 panic(todo("", v)) 457 } 458 case uint8: 459 switch len(v) { 460 case 1: 461 if _, err := fmt.Fprintf(f.w, "%d", v[0]); err != nil { 462 panic(todo("", err)) 463 } 464 case 2: 465 if _, err := fmt.Fprintf(f.w, "%*d", v[1], v[0]); err != nil { 466 panic(todo("", err)) 467 } 468 default: 469 panic(todo("", v)) 470 } 471 case uint16: 472 switch len(v) { 473 case 1: 474 if _, err := fmt.Fprintf(f.w, "%d", v[0]); err != nil { 475 panic(todo("", err)) 476 } 477 case 2: 478 if _, err := fmt.Fprintf(f.w, "%*d", v[1], v[0]); err != nil { 479 panic(todo("", err)) 480 } 481 default: 482 panic(todo("", v)) 483 } 484 case int32, int: 485 switch len(v) { 486 case 1: 487 if _, err := fmt.Fprintf(f.w, "%d", v[0]); err != nil { 488 panic(todo("", err)) 489 } 490 case 2: 491 if _, err := fmt.Fprintf(f.w, "%*d", v[1], v[0]); err != nil { 492 panic(todo("", err)) 493 } 494 default: 495 panic(todo("", v)) 496 } 497 case float32: 498 switch len(v) { 499 case 1: 500 if _, err := fmt.Fprintf(f.w, "%g", v[0]); err != nil { 501 panic(todo("", err)) 502 } 503 case 2: 504 if _, err := fmt.Fprintf(f.w, "%*e", v[1], v[0]); err != nil { 505 panic(todo("", err)) 506 } 507 case 3: 508 mw := v[1].(int) 509 nw := v[2].(int) 510 if _, err := fmt.Fprintf(f.w, "%*.*f", mw, nw, v[0]); err != nil { 511 panic(todo("", err)) 512 } 513 default: 514 panic(todo("", v)) 515 } 516 case float64: 517 switch len(v) { 518 case 1: 519 if _, err := fmt.Fprintf(f.w, "%g", v[0]); err != nil { 520 panic(todo("", err)) 521 } 522 case 2: 523 if _, err := fmt.Fprintf(f.w, "%*e", v[1], v[0]); err != nil { 524 panic(todo("", err)) 525 } 526 case 3: 527 mw := v[1].(int) 528 nw := v[2].(int) 529 if _, err := fmt.Fprintf(f.w, "%*.*f", mw, nw, v[0]); err != nil { 530 panic(todo("", err)) 531 } 532 default: 533 panic(todo("", v)) 534 } 535 default: 536 panic(todo("%T %v", x, v)) 537 } 538 } 539 } 540 541 func (f *textFile) Writeln(args ...interface{}) { 542 f.Write(args...) 543 if _, err := fmt.Fprintln(f.w); err != nil { 544 panic(todo("", err)) 545 } 546 } 547 548 type binaryFile struct { 549 *file 550 } 551 552 // NewBinaryFile returns a File suitable for Pascal file type 'file of T'. 553 func NewBinaryFile(r io.Reader, w io.Writer, sizeofT int, open func(string) (io.Reader, error)) File { 554 return &binaryFile{ 555 &file{ 556 buf: make([]byte, sizeofT), 557 r0: r, 558 reset: open, 559 w0: w, 560 }, 561 } 562 } 563 564 func (f *binaryFile) Write(args ...interface{}) { 565 switch len(f.buf) { 566 case 1: 567 for _, v := range args { 568 switch x := v.(type) { 569 case int32: 570 f.buf[0] = byte(x) 571 case int: 572 f.buf[0] = byte(x) 573 case byte: 574 f.buf[0] = x 575 case int16: 576 f.buf[0] = byte(x) 577 case uint16: 578 f.buf[0] = byte(x) 579 default: 580 panic(todo("%T", x)) 581 } 582 f.Put() 583 } 584 default: 585 panic(todo("", len(f.buf))) 586 } 587 } 588 589 func (f *binaryFile) Put() { 590 // trc("WR %#04x |% x| %q", f.dbgOff, f.buf, f.name) 591 f.dbgOff += len(f.buf) 592 if c, err := f.w.Write(f.buf[:]); c != len(f.buf) { 593 panic(todo("", err)) 594 } 595 } 596 597 func (f *binaryFile) Writeln(args ...interface{}) { 598 panic(todo("")) 599 } 600 601 type poolFile struct { 602 *file 603 } 604 605 // NewPoolFile returns a read only File with a string pool. 606 func NewPoolFile(pool string) File { 607 return &poolFile{ 608 &file{ 609 buf: make([]byte, 1), 610 isText: true, 611 r: strings.NewReader(pool), 612 }, 613 } 614 } 615 616 func (f *poolFile) Close() { 617 f.atEOF = true 618 } 619 620 func (f *poolFile) ErStat() int32 { 621 return 0 622 } 623 624 func (f *poolFile) Reset(args ...interface{}) { 625 f.atEOF = false 626 f.atEOLN = false 627 switch len(args) { 628 case 2: // eg. ["MFbases:MF.POOL", "/O"] 629 f.r.(*strings.Reader).Seek(0, io.SeekStart) 630 f.Get() 631 default: 632 panic(todo("%v", args)) 633 } 634 } 635 636 func (f *poolFile) Write(args ...interface{}) { 637 panic(todo("")) 638 } 639 640 func (f *poolFile) Writeln(args ...interface{}) { 641 panic(todo("")) 642 }