dForth

Forth-like interpreter in go
Log | Files | Refs

dForth.go (6926B)


      1 package main
      2 
      3 import (
      4 	"io"
      5 	"fmt"
      6 	"os"
      7 	"strconv"
      8 	"encoding/binary"
      9 )
     10 
     11 var le = binary.LittleEndian
     12 
     13 const (
     14 	pcpt = 0
     15 	stpt = 4
     16 	rspt = 8
     17 	lwpt = 12
     18 	dtpt = 16
     19 	stackstart = 64
     20 	rstackstart = 512
     21 	dictstart = 1024
     22 	initmemsize = 4096
     23 )
     24 
     25 type Word struct {
     26 	Name string
     27 	Builtin func() error
     28 	Addr int32
     29 	Immediate bool
     30 }
     31 
     32 func NewWord(name string, builtin func() error) *Word {
     33 	word := new(Word)
     34 	word.Name = name
     35 	word.Builtin = builtin
     36 	word.Immediate = false
     37 	return word
     38 }
     39 
     40 type Forth struct {
     41 	in io.Reader
     42 	out io.Writer
     43 	Dict []*Word
     44 	Mem []byte
     45 }
     46 
     47 func NewForth() *Forth {
     48 	forth := new(Forth)
     49 	forth.Dict = make([]*Word, 0)
     50 	forth.Dict = append(forth.Dict,
     51 		NewWord("_read", forth.biRead),
     52 		NewWord("_call", forth.biCall),
     53 		NewWord("_lit",  forth.biLit),
     54 		NewWord("_jump", forth.biJump),
     55 		NewWord("_ret",  forth.biRet),
     56 		NewWord(".",     forth.biDot),
     57 		NewWord("@",     forth.biFetch),
     58 		NewWord("!",     forth.biStore),
     59 		NewWord("+",     forth.biAdd),
     60 		NewWord("-",     forth.biSub),
     61 		NewWord("_nwd",  forth.biNewWord),
     62 		NewWord("immediate", forth.biImmediate),
     63 		NewWord("_cmpl", forth.biCompile),
     64 	)
     65 	forth.Mem = make([]byte, initmemsize)
     66 	_ = forth.memStore(stpt, stackstart)
     67 	_ = forth.memStore(rspt, rstackstart)
     68 	_ = forth.memStore(dtpt, dictstart)
     69 	_ = forth.memStore(lwpt, 0)
     70 	_ = forth.memStore(pcpt, dictstart)
     71 	forth.memCompile(
     72 	  0,            /*  _read         */
     73 	  2, dictstart, /* _lit dictstart */
     74 	  2, 0,         /* _lit 0         */
     75 	  3,            /* _jump          */
     76 	)
     77 	return forth
     78 }
     79 
     80 func (forth *Forth) memFetch(pt int32) (int32, error) {
     81 	if (pt < 0) && (int(pt) > len(forth.Mem) - 4) {
     82 		return 0, fmt.Errorf("memFetch: pt out of bonds")
     83 	}
     84 	val := int32(binary.LittleEndian.Uint32(forth.Mem[pt:]))
     85 	return val, nil
     86 }
     87 
     88 func (forth *Forth) memStore(pt, val int32) error {
     89 	if (pt < 0) {
     90 		return fmt.Errorf("memStore: pt out of bonds")
     91 	}
     92 	if int(pt) > len(forth.Mem) - 4 {
     93 		forth.Mem = append(forth.Mem,
     94 		  make([]byte, int(pt) - len(forth.Mem) + 4)...)
     95 	}
     96 	binary.LittleEndian.PutUint32(forth.Mem[pt:], uint32(val))
     97 	return nil
     98 }
     99 
    100 func (forth *Forth) memCompile(codes ...int32) {
    101 	dt, _ := forth.memFetch(dtpt)
    102 	for i, code := range codes {
    103 		_ = forth.memStore(int32(int(dt) + i * 4), code)
    104 	}
    105 	forth.memStore(dtpt, int32(int(dt) + len(codes) * 4))
    106 }
    107 
    108 func (forth *Forth) Step() error {
    109 	pc, err := forth.memFetch(pcpt);
    110 	inst, _ := forth.memFetch(pc);
    111 	_ = forth.memStore(pcpt, pc + 4)
    112 	if (inst < 0) || (int(inst) > len(forth.Dict)) {
    113 		return fmt.Errorf("invalid inst %d, pc %d",
    114 		  inst, pc)
    115 	}
    116 	w := forth.Dict[inst]
    117 	if w.Builtin != nil {
    118 		err = w.Builtin()
    119 	} else {
    120 		forth.Push(rspt, pc + 4)
    121 		pc = w.Addr
    122 	}
    123 	return err
    124 }
    125 
    126 func (forth *Forth) Push(pt, val int32) {
    127 	tos, err := forth.memFetch(pt)
    128 	if err != nil {
    129 		panic(err)
    130 	}
    131 	if err = forth.memStore(tos, val); err != nil {
    132 		panic(err)
    133 	}
    134 	if err = forth.memStore(pt, tos + 4); err != nil {
    135 		panic(err)
    136 	}
    137 }
    138 
    139 func (forth *Forth) Pop(pt int32) int32 {
    140 	tos, err := forth.memFetch(pt)
    141 	if err != nil {
    142 		panic(err)
    143 	}
    144 	tos -= 4
    145 	val, err := forth.memFetch(tos)
    146 	if err != nil {
    147 		panic(err)
    148 	}
    149 	if err = forth.memStore(pt, tos); err != nil {
    150 		panic(err)
    151 	}
    152 	return val
    153 }
    154 
    155 func (forth *Forth) Exec(w *Word) error {
    156 	if w.Builtin != nil {
    157 		return w.Builtin()
    158 	} else {
    159 		pc, _ := forth.memFetch(pcpt)
    160 		forth.Push(rspt, pc)
    161 		_ = forth.memStore(pcpt, w.Addr)
    162 	}
    163 	return nil	
    164 }
    165 
    166 func (forth *Forth) biDot() error {
    167 	val := forth.Pop(stpt)
    168 	fmt.Fprint(forth.out, val, " ")
    169 	return nil
    170 }
    171 
    172 func (forth *Forth) biFetch() error {
    173 	addr := forth.Pop(stpt)
    174 	val, err := forth.memFetch(addr)
    175 	if err == nil {
    176 		forth.Push(stpt, val)
    177 	}
    178 	return err
    179 }
    180 
    181 func (forth *Forth) biStore() error {
    182 	addr := forth.Pop(stpt)
    183 	val := forth.Pop(stpt)
    184 	err := forth.memStore(addr, val)
    185 	return err
    186 }
    187 
    188 func (forth *Forth) biRead() error {
    189 	var (
    190 		err, nerr error
    191 		s string
    192 		i, id, num int
    193 		w *Word
    194 	)
    195 	if _, err = fmt.Fscan(forth.in, &s); err != nil {
    196 		return err
    197 	}
    198 	/* We shouldn't get empty reads, but on 9front
    199 	 * we somehow do. (as of 18.10.2021)
    200 	 * Let's filter them out.
    201 	 */
    202 	if s == "" {
    203 		forth.memCompile(4) /* 4 is _ret */
    204 		return nil
    205 	}
    206 	id = -1
    207 	for i = len(forth.Dict); i > 0; i-- {
    208 		w = forth.Dict[i - 1]
    209 		if (s == w.Name) {
    210 			id = i - 1
    211 			break;
    212 		}
    213 	}
    214 	if id >= 0 {
    215 		return forth.Exec(w)
    216 	} else {
    217 		if num, nerr = strconv.Atoi(s); nerr != nil {
    218 			fmt.Fprintln(forth.out, "?")
    219 		} else {
    220 			forth.Push(stpt, int32(num))
    221 		}
    222 	}
    223 	return nil
    224 }
    225 
    226 func (forth *Forth) biCall() error {
    227 	/* do we need _call?
    228 	 * can we just treat opcode as call
    229 	 * if it's >= dictstart ?
    230 	 */
    231 	pc, err  := forth.memFetch(pcpt)
    232 	if err != nil {
    233 		return err
    234 	}
    235 	forth.Push(rspt, pc + 4)
    236 	jmp := forth.Pop(stpt)
    237 	err = forth.memStore(pcpt, jmp)
    238 	return err
    239 }
    240 
    241 func (forth *Forth) biRet() error {
    242 	jmp := forth.Pop(rspt)
    243 	err := forth.memStore(pcpt, jmp)
    244 	return err
    245 }
    246 
    247 func (forth *Forth) biLit() error {
    248 	pc, err := forth.memFetch(pcpt)
    249 	if err != nil {
    250 		return err
    251 	}
    252 	val, err := forth.memFetch(pc)
    253 	_ = forth.memStore(pcpt, pc + 4)
    254 	forth.Push(stpt, val)
    255 	return err
    256 }
    257 
    258 func (forth *Forth) biJump() error {
    259 	var err error
    260 	cond := forth.Pop(stpt)
    261 	addr := forth.Pop(stpt)
    262 	if cond == 0 {
    263 		err = forth.memStore(pcpt, addr)
    264 	}
    265 	return err
    266 }
    267 
    268 func (forth *Forth) biAdd() error {
    269 	x := forth.Pop(stpt)
    270 	y := forth.Pop(stpt)
    271 	forth.Push(stpt, x + y)
    272 	return nil
    273 }
    274 
    275 func (forth *Forth) biSub() error {
    276 	x := forth.Pop(stpt)
    277 	y := forth.Pop(stpt)
    278 	forth.Push(stpt, y - x)
    279 	return nil
    280 }
    281 
    282 func (forth *Forth) biNewWord() error {
    283 	var (
    284 		err error
    285 		s string
    286 		w *Word
    287 	)
    288 	if _, err = fmt.Fscan(forth.in, &s); err != nil {
    289 		return err
    290 	}
    291 	w = NewWord(s, nil)
    292 	w.Addr, _ = forth.memFetch(dtpt)
    293 	forth.Dict = append(forth.Dict, w)
    294 	forth.Push(stpt, w.Addr)
    295 	return nil
    296 }
    297 
    298 func (forth *Forth) biImmediate() error {
    299 	forth.Dict[len(forth.Dict) - 1].Immediate = true
    300 	return nil
    301 }
    302 
    303 func (forth *Forth) biCompile() error {
    304 	var (
    305 		i int
    306 		s string
    307 		w *Word
    308 	)
    309 	s = ""
    310 	for ;; {
    311 		if _, err := fmt.Fscan(forth.in, &s); err != nil {
    312 			return err
    313 		}
    314 		if s == ";" {
    315 			break
    316 		}
    317 		w = nil
    318 		for i = len(forth.Dict); i > 0; i-- {
    319 			if s == forth.Dict[i-1].Name {
    320 				w = forth.Dict[i-1]
    321 				break
    322 			}
    323 		}
    324 		if w != nil {
    325 			if w.Immediate {
    326 				forth.Exec(w)
    327 			} else {
    328 				forth.memCompile(int32(i - 1))
    329 			}
    330 		} else {
    331 			if num, err := strconv.Atoi(s); err != nil {
    332 				fmt.Fprintln(forth.out, "?")
    333 				/* TODO: we should revert changes to before compilation start */
    334 				return nil
    335 			} else {
    336 				forth.memCompile(2 , int32(num)) /* 2 is _lit */
    337 			}
    338 		}
    339 	}
    340 	forth.memCompile(4) /* 4 is _ret */
    341 	return nil
    342 }
    343 
    344 func (forth *Forth) Run(in io.Reader, out io.Writer) error {
    345 	var err error
    346 	forth.in = in
    347 	forth.out = out
    348 	for err == nil {
    349 		err = forth.Step()
    350 	}
    351 	return err
    352 }
    353 
    354 func main() {
    355 	fmt.Println("Welcome to dForth.")
    356 	forth := NewForth()
    357 	err := forth.Run(os.Stdin, os.Stdout)
    358 	fmt.Println("done: ", err)
    359 }