misc/kforth: Part 0x04 - parsing numerics.
This commit is contained in:
		
							parent
							
								
									0ae7d49593
								
							
						
					
					
						commit
						505d71906c
					
				
							
								
								
									
										1
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										1
									
								
								Makefile
								
								
								
								
							| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
CXXSTD :=	c++14
 | 
					CXXSTD :=	c++14
 | 
				
			||||||
CXXFLAGS :=	-std=$(CXXSTD) -Wall -Werror -g -O0
 | 
					CXXFLAGS :=	-std=$(CXXSTD) -Wall -Werror -g -O0
 | 
				
			||||||
OBJS :=		linux/io.o	\
 | 
					OBJS :=		linux/io.o	\
 | 
				
			||||||
 | 
							io.o		\
 | 
				
			||||||
		parser.o	\
 | 
							parser.o	\
 | 
				
			||||||
		kforth.o
 | 
							kforth.o
 | 
				
			||||||
TARGET :=	kforth
 | 
					TARGET :=	kforth
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								defs.h
								
								
								
								
							
							
						
						
									
										3
									
								
								defs.h
								
								
								
								
							| 
						 | 
					@ -3,6 +3,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
#include "linux/defs.h"
 | 
					#include "linux/defs.h"
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					typedef int KF_INT;
 | 
				
			||||||
 | 
					constexpr uint8_t STACK_SIZE = 16;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr size_t	MAX_TOKEN_LENGTH = 16;
 | 
					constexpr size_t	MAX_TOKEN_LENGTH = 16;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@ Contents:
 | 
				
			||||||
   part-0x01
 | 
					   part-0x01
 | 
				
			||||||
   part-0x02
 | 
					   part-0x02
 | 
				
			||||||
   part-0x03
 | 
					   part-0x03
 | 
				
			||||||
 | 
					   part-0x04
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Indices and tables
 | 
					Indices and tables
 | 
				
			||||||
==================
 | 
					==================
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,313 @@
 | 
				
			||||||
 | 
					Write You a Forth, 0x04
 | 
				
			||||||
 | 
					-----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:date: 2018-02-23 19:20
 | 
				
			||||||
 | 
					:tags: wyaf, forth
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So, I lied about words being next. When I thought about it some more, what I
 | 
				
			||||||
 | 
					really need to do is start adding the stack in and adding support for parsing
 | 
				
			||||||
 | 
					numerics. I'll start with the stack, because it's pretty straightforward.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I've added a new definition: ``constexpr uint8_t STACK_SIZE = 128``. This goes
 | 
				
			||||||
 | 
					in the ``linux/defs.h``, and the ``#else`` in the top ``defs.h`` will set a
 | 
				
			||||||
 | 
					smaller stack size for other targets. I've also defined a type called ``KF_INT``
 | 
				
			||||||
 | 
					that, on Linux, is a ``uint32_t``::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        index 4dcc540..e070d27 100644
 | 
				
			||||||
 | 
					        --- a/defs.h
 | 
				
			||||||
 | 
					        +++ b/defs.h
 | 
				
			||||||
 | 
					        @@ -3,6 +3,9 @@
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        #ifdef __linux__
 | 
				
			||||||
 | 
					        #include "linux/defs.h"
 | 
				
			||||||
 | 
					        +#else
 | 
				
			||||||
 | 
					        +typedef int KF_INT;
 | 
				
			||||||
 | 
					        +constexpr uint8_t STACK_SIZE = 16;
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        constexpr size_t       MAX_TOKEN_LENGTH = 16;
 | 
				
			||||||
 | 
					        diff --git a/linux/defs.h b/linux/defs.h
 | 
				
			||||||
 | 
					        index 57cdaeb..3740f5a 100644
 | 
				
			||||||
 | 
					        --- a/linux/defs.h
 | 
				
			||||||
 | 
					        +++ b/linux/defs.h
 | 
				
			||||||
 | 
					        @@ -4,4 +4,7 @@
 | 
				
			||||||
 | 
					        #include <stddef.h>
 | 
				
			||||||
 | 
					        #include <stdint.h>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        +typedef int32_t KF_INT;
 | 
				
			||||||
 | 
					        +constexpr uint8_t      STACK_SIZE = 128;
 | 
				
			||||||
 | 
					        +
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					        \ No newline at end of file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It seems useful to be able to adapt the kind of numbers supported; an AVR might do
 | 
				
			||||||
 | 
					better with 16-bit integers, for example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``stack.h``
 | 
				
			||||||
 | 
					^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The stack is going to be templated, because we'll need a ``double`` stack later
 | 
				
			||||||
 | 
					for floating point and a return address stack later. This means everything will
 | 
				
			||||||
 | 
					go under ``stack.h``. This is a pretty simple implementation that's CS 101 material;
 | 
				
			||||||
 | 
					I've opted to have the interface return ``bool``\ s for everything to indicate stack
 | 
				
			||||||
 | 
					overflow and underflow and out of bounds::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #ifndef __KF_STACK_H__
 | 
				
			||||||
 | 
					        #define __KF_STACK_H__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #include "defs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        template <typename T>
 | 
				
			||||||
 | 
					        class Stack {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					                bool   push(T val);
 | 
				
			||||||
 | 
					                bool   pop(T &val);
 | 
				
			||||||
 | 
					                bool   get(size_t, T &);
 | 
				
			||||||
 | 
					                size_t size(void) { return this->arrlen; };
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					                T arr[STACK_SIZE];
 | 
				
			||||||
 | 
					                size_t arrlen;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // push returns false if there was a stack overflow.
 | 
				
			||||||
 | 
					        template <typename T>
 | 
				
			||||||
 | 
					        bool
 | 
				
			||||||
 | 
					        Stack<T>::push(T val)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                if ((this->arrlen + 1) > STACK_SIZE) {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                this->arr[this->arrlen++] = val;
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // pop returns false if there was a stack underflow.
 | 
				
			||||||
 | 
					        template <typename T>
 | 
				
			||||||
 | 
					        bool
 | 
				
			||||||
 | 
					        Stack<T>::pop(T &val)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                if (this->arrlen == 0) {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                val = this->arr[this->arrlen - 1];
 | 
				
			||||||
 | 
					                this->arrlen--;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // get returns false on invalid bounds.
 | 
				
			||||||
 | 
					        template <typename T>
 | 
				
			||||||
 | 
					        bool
 | 
				
			||||||
 | 
					        Stack<T>::get(size_t i, T &val)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                if (i > this->arrlen) {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                val = this->arr[i];
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #endif // __KF_STACK_H__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'll put a ``Stack<KF_INT>`` in ``kforth.cc`` later on. For now, this gives me
 | 
				
			||||||
 | 
					an interface for the numeric parser to push a number onto the stack.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``parse_num``
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It seems like the best place for this is in ``parser.cc`` --- though I might
 | 
				
			||||||
 | 
					move into a token processor later. The definition for this goes in ``parser.h``,
 | 
				
			||||||
 | 
					and the body is in ``parser.cc``::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // parse_num tries to parse the token as a signed base 10 number, 
 | 
				
			||||||
 | 
					        // pushing it onto the stack if needed.
 | 
				
			||||||
 | 
					        bool
 | 
				
			||||||
 | 
					        parse_num(struct Token *token, Stack<KF_INT> &s)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                KF_INT	n = 0;
 | 
				
			||||||
 | 
					                uint8_t i = 0;
 | 
				
			||||||
 | 
					                bool    sign = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It turns out you can't parse a zero-length token as a number...
 | 
				
			||||||
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (token->length == 0) {
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I'll need to invert the number later if it's negative, but it's worth checking
 | 
				
			||||||
 | 
					the first character to see if it's negative.
 | 
				
			||||||
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (token->token[i] == '-') {
 | 
				
			||||||
 | 
					                        i++;
 | 
				
			||||||
 | 
					                        sign = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Parsing is done by checking whether each character is within the range of the ASCII
 | 
				
			||||||
 | 
					numeral values. Later on, I might add in separate functions for processing base 10
 | 
				
			||||||
 | 
					and base 16 numbers, and decide which to use based on a prefix (like ``0x``). If the
 | 
				
			||||||
 | 
					character is between those values, then the working number is multiplied by 10 and
 | 
				
			||||||
 | 
					the digit added.
 | 
				
			||||||
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (i < token->length) {
 | 
				
			||||||
 | 
					                        if (token->token[i] < '0') {
 | 
				
			||||||
 | 
					                                return false;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (token->token[i] > '9') {
 | 
				
			||||||
 | 
					                                return false;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        n *= 10;
 | 
				
			||||||
 | 
					                        n += (uint8_t)(token->token[i] - '0');
 | 
				
			||||||
 | 
					                        i++;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If it was a negative number, then the working number has to be inverted::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (sign) {
 | 
				
			||||||
 | 
					                        n *= -1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Finally, return the result of pushing the number on the stack. One thing that
 | 
				
			||||||
 | 
					might come back to get me later is that this makes it impossible to tell if a
 | 
				
			||||||
 | 
					failure to parse the number is due to an invalid number or due to a stack
 | 
				
			||||||
 | 
					overflow. This will be a good candidate for revisiting later.
 | 
				
			||||||
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return s.push(n);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``io.cc``
 | 
				
			||||||
 | 
					^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Conversely, it'll be useful to write a number to an ``IO`` interface. It
 | 
				
			||||||
 | 
					*seems* more useful right now to just provide a number → I/O function, but
 | 
				
			||||||
 | 
					that'll be easily adapted to a number → buffer function later. This will add
 | 
				
			||||||
 | 
					a real function to ``io.h``, which will require a corresponding ``io.cc``
 | 
				
			||||||
 | 
					(which also needs to be added to the ``Makefile``)::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #include "defs.h"
 | 
				
			||||||
 | 
					        #include "io.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        #include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void
 | 
				
			||||||
 | 
					        write_num(IO &interface, KF_INT n)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Through careful scientific study, I have determined that most number of digits
 | 
				
			||||||
 | 
					that a 32-bit integer needs is 10 bytes (sans the sign!). This will absolutely
 | 
				
			||||||
 | 
					need to be changed if ``KF_INT`` is ever moved to 64-bit (or larger!) numbers.
 | 
				
			||||||
 | 
					There's a TODO in the actual source code that notes this. ::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                char buf[10];
 | 
				
			||||||
 | 
					                uint8_t i = 10;
 | 
				
			||||||
 | 
					                memset(buf, 0, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Because this is going out to an I/O interface, I don't need to store the sign
 | 
				
			||||||
 | 
					in the buffer itself and can just print it and invert the number. Inverting is
 | 
				
			||||||
 | 
					important; I ran into a bug earlier where I didn't invert it and my subtractions
 | 
				
			||||||
 | 
					below were correspondingly off.
 | 
				
			||||||
 | 
					::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (n < 0) {
 | 
				
			||||||
 | 
					                        interface.wrch('-');
 | 
				
			||||||
 | 
					                        n *= -1;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The buffer has to be filled from the end to the beginning to do the inverse of
 | 
				
			||||||
 | 
					the parsing method::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                while (n != 0) {
 | 
				
			||||||
 | 
					                        char ch = (n % 10) + '0';
 | 
				
			||||||
 | 
					                        buf[i--] = ch;
 | 
				
			||||||
 | 
					                        n /= 10;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					But then it can be just dumped to the interface::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                interface.wrbuf(buf+i, 11-i);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``kforth.cc``
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					And now I come to the fun part: adding the stack in. After including ``stack.h``,
 | 
				
			||||||
 | 
					I've added a stack implementation to the top of the file::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // dstack is the data stack.
 | 
				
			||||||
 | 
					        static Stack<KF_INT>	dstack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					It's kind of useful to be able to print the stack::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static void
 | 
				
			||||||
 | 
					        write_dstack(IO &interface)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					                KF_INT	tmp;
 | 
				
			||||||
 | 
					                interface.wrch('<');
 | 
				
			||||||
 | 
					                for (size_t i = 0; i < dstack.size(); i++) {
 | 
				
			||||||
 | 
					                        if (i > 0) {
 | 
				
			||||||
 | 
					                                interface.wrch(' ');
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        dstack.get(i, tmp);
 | 
				
			||||||
 | 
					                        write_num(interface, tmp);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                interface.wrch('>');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Surrounding the stack in angle brackets is a cool stylish sort of thing, I
 | 
				
			||||||
 | 
					guess. All this is no good if the interpreter isn't actually hooked up to the
 | 
				
			||||||
 | 
					number parser::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // The new while loop in the parser function in kforth.cc:
 | 
				
			||||||
 | 
					        while ((result = parse_next(buf, buflen, &offset, &token)) == PARSE_OK) {
 | 
				
			||||||
 | 
					                interface.wrbuf((char *)"token: ", 7);
 | 
				
			||||||
 | 
					                interface.wrbuf(token.token, token.length);
 | 
				
			||||||
 | 
					                interface.wrln((char *)".", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!parse_num(&token, dstack)) {
 | 
				
			||||||
 | 
					                        interface.wrln((char *)"failed to parse numeric", 23);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // Temporary hack until the interpreter is working further.
 | 
				
			||||||
 | 
					                if (match_token(token.token, token.length, bye, 3)) {
 | 
				
			||||||
 | 
					                        interface.wrln((char *)"Goodbye!", 8);
 | 
				
			||||||
 | 
					                        exit(0);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					But does it blend?
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Hopefully this works::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ~/code/kforth (0) $ make
 | 
				
			||||||
 | 
					        g++ -std=c++14 -Wall -Werror -g -O0   -c -o linux/io.o linux/io.cc
 | 
				
			||||||
 | 
					        g++ -std=c++14 -Wall -Werror -g -O0   -c -o io.o io.cc
 | 
				
			||||||
 | 
					        g++ -std=c++14 -Wall -Werror -g -O0   -c -o parser.o parser.cc
 | 
				
			||||||
 | 
					        g++ -std=c++14 -Wall -Werror -g -O0   -c -o kforth.o kforth.cc
 | 
				
			||||||
 | 
					        g++  -o kforth linux/io.o io.o parser.o kforth.o
 | 
				
			||||||
 | 
					        ~/code/kforth (0) $ ./kforth
 | 
				
			||||||
 | 
					        kforth interpreter
 | 
				
			||||||
 | 
					        <>
 | 
				
			||||||
 | 
					        ? 2 -2 30 1000 -1010
 | 
				
			||||||
 | 
					        token: 2.
 | 
				
			||||||
 | 
					        token: -2.
 | 
				
			||||||
 | 
					        token: 30.
 | 
				
			||||||
 | 
					        token: 1000.
 | 
				
			||||||
 | 
					        token: -1010.
 | 
				
			||||||
 | 
					        ok.
 | 
				
			||||||
 | 
					        <2 -2 30 1000 -1010>
 | 
				
			||||||
 | 
					        ? bye
 | 
				
			||||||
 | 
					        token: bye.
 | 
				
			||||||
 | 
					        failed to parse numeric
 | 
				
			||||||
 | 
					        Goodbye!
 | 
				
			||||||
 | 
					        ~/code/kforth (0) $
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					So there's that. Okay, next time *for real* I'll do a vocabulary thing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					#include "defs.h"
 | 
				
			||||||
 | 
					#include "io.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					write_num(IO &interface, KF_INT n)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO(kyle): make the size of the buffer depend on the size of 
 | 
				
			||||||
 | 
						// KF_INT.
 | 
				
			||||||
 | 
						char buf[10];
 | 
				
			||||||
 | 
						uint8_t i = 10;
 | 
				
			||||||
 | 
						memset(buf, 0, i);
 | 
				
			||||||
 | 
						if (n < 0) {
 | 
				
			||||||
 | 
							interface.wrch('-');
 | 
				
			||||||
 | 
							n *= -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (n != 0) {
 | 
				
			||||||
 | 
							char ch = (n % 10) + '0';
 | 
				
			||||||
 | 
							buf[i--] = ch;
 | 
				
			||||||
 | 
							n /= 10;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						interface.wrbuf(buf+i, 11-i);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										3
									
								
								io.h
								
								
								
								
							
							
						
						
									
										3
									
								
								io.h
								
								
								
								
							| 
						 | 
					@ -21,4 +21,7 @@ public:
 | 
				
			||||||
	virtual void	wrln(char *buf, size_t len) = 0;
 | 
						virtual void	wrln(char *buf, size_t len) = 0;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void	write_num(IO &, KF_INT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // __KF_IO_H__
 | 
					#endif // __KF_IO_H__
 | 
				
			||||||
							
								
								
									
										28
									
								
								kforth.cc
								
								
								
								
							
							
						
						
									
										28
									
								
								kforth.cc
								
								
								
								
							| 
						 | 
					@ -1,7 +1,9 @@
 | 
				
			||||||
#include "io.h"
 | 
					#include "io.h"
 | 
				
			||||||
#include "parser.h"
 | 
					#include "parser.h"
 | 
				
			||||||
 | 
					#include "stack.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef __linux__
 | 
					#ifdef __linux__
 | 
				
			||||||
#include "linux.h"
 | 
					#include "linux.h"
 | 
				
			||||||
| 
						 | 
					@ -10,6 +12,26 @@
 | 
				
			||||||
static char     ok[] = "ok.\n";
 | 
					static char     ok[] = "ok.\n";
 | 
				
			||||||
static char	bye[] = "bye";
 | 
					static char	bye[] = "bye";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// dstack is the data stack.
 | 
				
			||||||
 | 
					static Stack<KF_INT>	dstack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					write_dstack(IO &interface)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	tmp;
 | 
				
			||||||
 | 
						interface.wrch('<');
 | 
				
			||||||
 | 
						for (size_t i = 0; i < dstack.size(); i++) {
 | 
				
			||||||
 | 
							if (i > 0) {
 | 
				
			||||||
 | 
								interface.wrch(' ');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dstack.get(i, tmp);
 | 
				
			||||||
 | 
							write_num(interface, tmp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						interface.wrch('>');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
parser(IO &interface, const char *buf, const size_t buflen)
 | 
					parser(IO &interface, const char *buf, const size_t buflen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -28,6 +50,10 @@ parser(IO &interface, const char *buf, const size_t buflen)
 | 
				
			||||||
		interface.wrbuf(token.token, token.length);
 | 
							interface.wrbuf(token.token, token.length);
 | 
				
			||||||
		interface.wrln((char *)".", 1);
 | 
							interface.wrln((char *)".", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!parse_num(&token, dstack)) {
 | 
				
			||||||
 | 
								interface.wrln((char *)"failed to parse numeric", 23);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Temporary hack until the interpreter is working further.
 | 
							// Temporary hack until the interpreter is working further.
 | 
				
			||||||
		if (match_token(token.token, token.length, bye, 3)) {
 | 
							if (match_token(token.token, token.length, bye, 3)) {
 | 
				
			||||||
			interface.wrln((char *)"Goodbye!", 8);
 | 
								interface.wrln((char *)"Goodbye!", 8);
 | 
				
			||||||
| 
						 | 
					@ -58,6 +84,8 @@ interpreter(IO &interface)
 | 
				
			||||||
	static char linebuf[81];
 | 
						static char linebuf[81];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (true) {
 | 
						while (true) {
 | 
				
			||||||
 | 
							write_dstack(interface);
 | 
				
			||||||
 | 
							interface.wrch('\n');
 | 
				
			||||||
		interface.wrch('?');
 | 
							interface.wrch('?');
 | 
				
			||||||
		interface.wrch(' ');
 | 
							interface.wrch(' ');
 | 
				
			||||||
		buflen = interface.rdbuf(linebuf, 80, true, '\n');
 | 
							buflen = interface.rdbuf(linebuf, 80, true, '\n');
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								linux.h
								
								
								
								
							
							
						
						
									
										2
									
								
								linux.h
								
								
								
								
							| 
						 | 
					@ -6,7 +6,5 @@
 | 
				
			||||||
// build support for linux
 | 
					// build support for linux
 | 
				
			||||||
#include "linux/io.h"
 | 
					#include "linux/io.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr uint8_t STACK_SIZE = 128;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // __KF_LINUX_H__
 | 
					#endif // __KF_LINUX_H__
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,4 +4,7 @@
 | 
				
			||||||
#include <stddef.h>
 | 
					#include <stddef.h>
 | 
				
			||||||
#include <stdint.h>
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef int32_t KF_INT;
 | 
				
			||||||
 | 
					constexpr uint8_t	STACK_SIZE = 128;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										38
									
								
								parser.cc
								
								
								
								
							
							
						
						
									
										38
									
								
								parser.cc
								
								
								
								
							| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
#include "defs.h"
 | 
					#include "defs.h"
 | 
				
			||||||
#include "parser.h"
 | 
					#include "parser.h"
 | 
				
			||||||
 | 
					#include "stack.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string.h>
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -68,4 +69,41 @@ parse_next(const char *buf, const size_t length, size_t *offset,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	*offset = cursor;
 | 
						*offset = cursor;
 | 
				
			||||||
	return PARSE_OK;
 | 
						return PARSE_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool
 | 
				
			||||||
 | 
					parse_num(struct Token *token, Stack<KF_INT> &s)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						KF_INT	n = 0;
 | 
				
			||||||
 | 
						uint8_t i = 0;
 | 
				
			||||||
 | 
						bool    sign = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (token->length == 0) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (token->token[i] == '-') {
 | 
				
			||||||
 | 
							i++;
 | 
				
			||||||
 | 
							sign = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (i < token->length) {
 | 
				
			||||||
 | 
							if (token->token[i] < '0') {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (token->token[i] > '9') {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							n *= 10;
 | 
				
			||||||
 | 
							n += (uint8_t)(token->token[i] - '0');
 | 
				
			||||||
 | 
							i++;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sign) {
 | 
				
			||||||
 | 
							n *= -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return s.push(n);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										5
									
								
								parser.h
								
								
								
								
							
							
						
						
									
										5
									
								
								parser.h
								
								
								
								
							| 
						 | 
					@ -2,6 +2,7 @@
 | 
				
			||||||
#define __KF_PARSER_H__
 | 
					#define __KF_PARSER_H__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "defs.h"
 | 
					#include "defs.h"
 | 
				
			||||||
 | 
					#include "stack.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Token {
 | 
					struct Token {
 | 
				
			||||||
	char	*token;
 | 
						char	*token;
 | 
				
			||||||
| 
						 | 
					@ -18,5 +19,9 @@ typedef enum _PARSE_RESULT_ : uint8_t {
 | 
				
			||||||
bool		match_token(const char *, const size_t, const char *, const size_t);
 | 
					bool		match_token(const char *, const size_t, const char *, const size_t);
 | 
				
			||||||
PARSE_RESULT	parse_next(const char *, const size_t, size_t *, struct Token *);
 | 
					PARSE_RESULT	parse_next(const char *, const size_t, size_t *, struct Token *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO(kyle): investigate a better return value, e.g. to differentiate between
 | 
				
			||||||
 | 
					// stack failures and parse failures.
 | 
				
			||||||
 | 
					bool		parse_num(struct Token *, Stack<KF_INT> &);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // __KF_PARSER_H__
 | 
					#endif // __KF_PARSER_H__
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,57 @@
 | 
				
			||||||
 | 
					#ifndef __KF_STACK_H__
 | 
				
			||||||
 | 
					#define __KF_STACK_H__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "defs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					class Stack {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					        bool   push(T val);
 | 
				
			||||||
 | 
						bool   pop(T &val);
 | 
				
			||||||
 | 
						bool   get(size_t, T &);
 | 
				
			||||||
 | 
						size_t size(void) { return this->arrlen; };
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						T arr[STACK_SIZE];
 | 
				
			||||||
 | 
						size_t arrlen;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// push returns false if there was a stack overflow.
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					bool
 | 
				
			||||||
 | 
					Stack<T>::push(T val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if ((this->arrlen + 1) > STACK_SIZE) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this->arr[this->arrlen++] = val;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// pop returns false if there was a stack underflow.
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					bool
 | 
				
			||||||
 | 
					Stack<T>::pop(T &val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (this->arrlen == 0) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						val = this->arr[this->arrlen - 1];
 | 
				
			||||||
 | 
						this->arrlen--;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// get returns false on invalid bounds.
 | 
				
			||||||
 | 
					template <typename T>
 | 
				
			||||||
 | 
					bool
 | 
				
			||||||
 | 
					Stack<T>::get(size_t i, T &val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (i > this->arrlen) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						val = this->arr[i];
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // __KF_STACK_H__
 | 
				
			||||||
		Loading…
	
		Reference in New Issue