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  }