├── .gitignore ├── README ├── goforth ├── main.go └── sample.fth /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | *.6 3 | .DS_Store 4 | 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | GoForth 2 | ======= 3 | 4 | I got really interested in Forth and thus I began making a parser, of sorts, in 5 | Go. Though I don't intend for it to catch on, it's still a nifty piece of code 6 | and can easily be disected if you want some Forth in your code. 7 | 8 | Currently Supports: 9 | . .S 0SP + - * / DUP ?DUP 2DUP PICK TUCK NIP 10 | DROP 2DROP OVER 2OVER SWAP 2SWAP 2SWAP ROT -ROT 11 | ( comments ) 12 | 13 | I added a "t" command that checks if the popped value is greater then the DataStack 14 | length so that if you need to make sure that you have at least 2 numbers on the 15 | stack, you can call " 2 t " and it will tell you there's not enough, and then 16 | crash and burn, as per Go. 17 | 18 | Enjoy! 19 | -- 20 | Artem Titoulenko -------------------------------------------------------------------------------- /goforth: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ox/GoForth/6b8ecaf2694a17bfe2ca07a72a5a1b2bf5a41b9a/goforth -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log"; 5 | "os"; 6 | "strconv"; 7 | "strings"; 8 | "bufio"; 9 | "flag"; 10 | "fmt"; 11 | "container/vector"; 12 | ) 13 | 14 | var ( 15 | in *bufio.Reader; 16 | commands vector.Vector; 17 | DataStack vector.Vector; 18 | ); 19 | 20 | var file *string = flag.String("file", "", "Define a Fourth src file to be read"); 21 | 22 | func main() { 23 | flag.Parse(); 24 | 25 | DataStack := new (vector.Vector); 26 | 27 | if *file != "" { 28 | file_to_read, err := os.OpenFile(*file, os.O_RDONLY, 0755); 29 | if file_to_read == nil { log.Fatal(err); } 30 | 31 | in = bufio.NewReader( file_to_read ); 32 | 33 | comment := false; //if we're currently in a comment 34 | 35 | for { 36 | dat, err := in.ReadString(' '); 37 | if err != nil { log.Fatal("can't read file string"); return; } 38 | 39 | if dat[0:len(dat)-1] == "(" { 40 | comment = true; 41 | } 42 | 43 | if comment == false && dat[0:len(dat)-1] != "" && dat[0:len(dat)-1] != " " { 44 | parse_forth(dat[0:len(dat)-1], DataStack); 45 | } 46 | 47 | if dat[0:len(dat)-1] == ")" { 48 | comment = false; 49 | } 50 | } 51 | } else { 52 | buf := bufio.NewReader(os.Stdin) 53 | 54 | for { 55 | fmt.Print("> "); 56 | read, err := buf.ReadString('\n') 57 | if err != nil { 58 | println() 59 | break 60 | } 61 | 62 | comms := strings.Split(read," ", -1) 63 | 64 | for i := 0; i < len(comms); i++ { 65 | if len(comms) == 0 || comms[i] == "" || comms[i] == " " { 66 | continue 67 | } 68 | 69 | parse_forth(strings.TrimSpace(comms[i]), DataStack) 70 | } 71 | } 72 | } 73 | } 74 | /* 75 | Experimental, session type operation (think python shell) 76 | 77 | 78 | else { 79 | in = bufio.NewReader(os.Stdin); 80 | for { 81 | dat, err := in.ReadString(' '); 82 | if err != nil { log.Stderr(err); return; } 83 | 84 | parse_forth(dat[0:len(dat)-1], DataStack); 85 | } 86 | } 87 | /**/ 88 | 89 | func check_stack_size( DataStack *vector.Vector, required int) bool { 90 | if DataStack.Len() < required { 91 | log.Fatal("Stack depth is less then " + string(required) + ". Operation is impossible"); 92 | return false; 93 | } 94 | 95 | return true; 96 | } 97 | 98 | func parse_forth(dat string, DataStack *vector.Vector) { 99 | L := DataStack.Len(); 100 | 101 | switch strings.TrimSpace(string(dat)) { 102 | case "": 103 | case "": 104 | return; 105 | case "t": 106 | //check the DataStack size using the popped value 107 | // if it passes, then the program continues 108 | minimum := int(DataStack.Pop().(float32)); 109 | if DataStack.Len() < minimum { 110 | log.Println("DataStack has not enough minerals (values)"); 111 | } 112 | case ".": 113 | log.Println(DataStack.Pop()); 114 | case "0SP": 115 | DataStack.Cut(0, L); 116 | case ".S": 117 | log.Println(DataStack); 118 | case "2/": 119 | DataStack.Push( DataStack.Pop().(float32) / 2); 120 | case "2*": 121 | DataStack.Push( DataStack.Pop().(float32) * 2); 122 | case "2-": 123 | DataStack.Push( DataStack.Pop().(float32) - 2); 124 | case "2+": 125 | DataStack.Push( DataStack.Pop().(float32) + 2); 126 | case "1-": 127 | DataStack.Push( DataStack.Pop().(float32) - 1); 128 | case "1+": 129 | DataStack.Push( DataStack.Pop().(float32) + 1); 130 | case "DUP": 131 | DataStack.Push( DataStack.Last() ); 132 | case "?DUP": 133 | if DataStack.Last().(float32) != 0 { DataStack.Push( DataStack.Last().(float32) ); } 134 | case "PICK": 135 | number := int(DataStack.Pop().(float32)) ; 136 | 137 | if number < L { 138 | DataStack.Push( DataStack.At(L - 1 - number).(float32) ); 139 | } else { 140 | log.Fatal("picking out of stack not allowed. Stack Length: " + string(L) + ". Selecting: " + string(number) + "."); 141 | return; 142 | } 143 | case "TUCK": 144 | DataStack.Insert(L - 2, int(DataStack.Last().(float32)) ); 145 | case "NIP": 146 | DataStack.Delete(L - 2); 147 | case "2DROP": 148 | DataStack.Pop(); DataStack.Pop(); 149 | case "2DUP": 150 | DataStack.Push(DataStack.At(L - 2)); 151 | DataStack.Push(DataStack.At(DataStack.Len() - 2)); 152 | case "DROP": 153 | DataStack.Pop(); 154 | case "OVER": 155 | DataStack.Push(DataStack.At(L - 2)); 156 | case "SWAP": 157 | l := DataStack.Len(); 158 | DataStack.Swap(l - 2, l - 1); 159 | case "*": 160 | num1 := DataStack.Pop().(float32); 161 | num2 := DataStack.Pop().(float32); 162 | DataStack.Push( num1 * num2 ); 163 | case "+": 164 | num1 := DataStack.Pop().(float32); 165 | num2 := DataStack.Pop().(float32); 166 | DataStack.Push( num1 + num2 ); 167 | case "-": 168 | num1 := DataStack.Pop().(float32); 169 | num2 := DataStack.Pop().(float32); 170 | DataStack.Push( num2 - num1 ); 171 | case "/": 172 | num1 := DataStack.Pop().(float32); 173 | num2 := DataStack.Pop().(float32); 174 | DataStack.Push( num2 / num1 ); 175 | case "-ROT": 176 | DataStack.Swap(L - 1, L - 2); 177 | DataStack.Swap(L - 2, L - 3); 178 | case "ROT": 179 | DataStack.Swap(L - 3, L - 2); 180 | DataStack.Swap(L - 2, L - 1); 181 | case "2OVER": 182 | DataStack.Push(DataStack.At(L - 4)); 183 | DataStack.Push(DataStack.At(DataStack.Len() - 4)); 184 | case "2SWAP": 185 | DataStack.Swap(L - 4, L - 2); 186 | DataStack.Swap(L - 3, L - 1); 187 | case "EMIT": 188 | log.Println( string([]byte{uint8(DataStack.Last().(float32))}) ); 189 | default: 190 | val, ok := strconv.Atof32(dat); 191 | 192 | if ok == nil { 193 | DataStack.Push( val ); 194 | } else { 195 | log.Println(ok); 196 | log.Fatalln("error, unknown token \""+dat+"\""); 197 | } 198 | } 199 | } 200 | 201 | -------------------------------------------------------------------------------- /sample.fth: -------------------------------------------------------------------------------- 1 | 1 .S ?DUP .S 0 ?DUP .S 0SP 2 | 3 | 1 2 3 -ROT .S 0SP 4 | 5 | 1 2 3 4 2SWAP .S 0SP 6 | 7 | 1 2 3 4 2OVER .S 0SP 8 | 9 | 1 2 2DUP .S 0SP 10 | 11 | 1 2 2DROP .S 0SP 12 | 13 | 1 2 NIP .S 0SP 14 | 15 | 1 2 TUCK .S 0SP 16 | 17 | 30 5 + 7 / . 18 | 19 | 7 2* 1+ . ( 15=7*2+1 ) 20 | 21 | 17 5 / . 22 | 23 | ( 1 2 t 2DUP .S ( it will never get to .S ) 24 | 25 | 72 EMIT 105 EMIT --------------------------------------------------------------------------------