Page 1 of 1

Writing An Interpreter Part I of V - The Interpreter Environment Rate Topic: ***** 1 Votes

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 04 February 2010 - 11:49 PM

*
POPULAR

Writing an Interpreter - Part I : The Interpreter Environment

A Brief History

Interpreters have been around for many years ... in fact almost as many years as I have been writing software. Interpreters are fed input from the user line by line and give editing facilities should you enter incorrect code. They report syntax errors immediately upon input so it is not possible to enter syntactically incorrect code (although some of the semantic errors are reported at run-time). One of the most common interpreters on early PC's was Basic which stood for Beginners All-purpose Symbolic Instruction Code.

The sophisticated Microsoft Visual Basic has it's roots in such early versions of Basic implemented on the IBM PC, and interpreters are still very much alive and well.

Prerequisites

Anybody that intends to write an interpreter has to understand the principles of parsing, expression evaluation and dynamic memory allocation. Without these prinicples, you will not be able to understand what the solutions to many of the problems that will be encountered in writing even the simplest intepreter.

I have chosen to elect a simple BASIC interpreter as our goal for the purpose of these tutorials, and we will hopefully end up with a working interpreter at the end of them.

The Interpreter Environment

Interpreters are not, generally speaking, stand-alone programs that the developer invokes to compile a piece of code. They also offer an Integrated Development Environment in much the same way as applications such as Visual Studio, Delphi and Eclipse. We will therefore need to write such an environment for our basic interpreted.

The aim of this tutorial is to write a simple line editor that gives us the ability to edit code and form a platform for our interpreter.

All of the code has to be portable (at least as portable as is possible) so the interpreter can be made to run on any OS with minimal change.

The Basic Line Editor

This will be a VEry Tiny LINe Editor - VETLINE (I love naming software) which gives you the ability to move up and down lines, list, insert and delete lines and modify lines. The commands will be simple:

U              will move up a line 
Un             will move up n lines
D              will move down a line
Dn             will move down n lines 
I              will place the editor in insert mode where lines may be entered
R              will remove the current line
Rn             will remove the n lines
C              will display the current line
O              will place the current line in 'overwrite mode' (not implemented in this version)
S              will show all lines
Sn             will show the next n lines (not implemented in this version)
Lfilename      will load a file from disc (not implemented in this version)
Sfilename      will save a file to disc (not implemented in this version)
X              will execute the source (not implemented in this version)



I have included the code for the line editor below. It has been tested, but may still contain errors ... which I am sure we will find as we develop the interpreter. The editor may also develop as we progress through these tutorials.

Here is the include file

#ifndef __LINE_EDITOR_INCLUDED__
#define __LINE_EDITOR_INCLUDED__

#pragma once

class line_editor {
	private:
		vector<string> line_table;
		vector<string>::iterator iterator;
		long current_line;
	public:
		line_editor() : current_line(-1) { };
		void command(string command);
};

#endif



Here is the code file

// VEry Tiny LINe Editor - version 1.0
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

using namespace std;

#include <cstdio>
#include "LineEditor.h"

// VETLINE editor - this is all straight forward code
void line_editor::command(string command_line) {
	char line_buffer[32];
	unsigned int command_line_length;
	unsigned char command_char;
	int command_count = 0;

	// Get the command from the command line
	command_line_length = command_line.length();
	if ( command_line_length == 0 ) return;
	
	// Command to upper case character
	command_char = (command_line[0]&0xDF);
	
	// Get the command count if there is one
	if ( command_line_length >= 1 ) {
		int char_index = 1;
		while ( char_index < command_line_length ) {
			unsigned char this_char = command_line[char_index++];
			if ( this_char >= '0' && this_char <= '9' ) {
				command_count = (command_count*10) + (this_char - '0');
			}
		}
	}
	
	// Default to a command count of one if the user
	// has not given us a count
	if ( command_count == 0 ) command_count = 1;
	
	// If the command is not insert and the list is empty
	// then inform the user of such and return to caller
	if ( current_line == -1 && command_char != 'I' ) {
		cout << "The file is empty" << endl;
		return;
	}

	// Process the command based upon the letter entered
	// We simply ignore commands that we do not recognise
	// for this version
	switch ( command_char ) {
	case 'Q': // Quit VETLINE
		{
			exit(0);
		}
	case 'U': // More up one or more lines
		{
			if ( current_line == 0 ) {
				cout << "At top of file" << endl;
				return;
			}
			current_line -= command_count;
			if ( current_line < 0 ) current_line = 0;
			sprintf(line_buffer, "%8d* ", current_line + 1);
			cout << line_buffer << line_table[current_line] << endl;
			return;
		}
	case 'D': // More down one or more lines
		{
			if ( (current_line+1) == line_table.size() ) {
				cout << "At end of file" << endl;
				return;
			}
			current_line += command_count;
			if ( (current_line+1) >= line_table.size() ) current_line = line_table.size()-1;
			sprintf(line_buffer, "%8d* ", current_line + 1);
			cout << line_buffer << line_table[current_line] << endl;
			return;
		}
		case 'I':  // Insert lines
		{
			string insertion_line;
			int line_number = current_line+1;
			int start_number = line_number;
			
			while ( true ) {
				current_line++;
				sprintf(line_buffer, "%8d  ", line_number + 1);
				cout << line_buffer;
				getline(cin, insertion_line);
				if ( cin.eof() ) break;
				line_table.insert(line_table.begin()+current_line, insertion_line);
				line_number++;
			}
			cin.clear();
			current_line = start_number;
			return;
		}
	case 'R': // Remove one line at a time (count is ignored for now)
		{
			if ( (current_line+1) == line_table.size() ) {
				if ( current_line == 0 ) {
					line_table.erase(line_table.begin()+current_line);
					current_line = -1;
					return;
				}
				cout << "At end of file" << endl;
				return;
			}
			line_table.erase(line_table.begin()+current_line);
			return;
		}
	case 'S': // Show lines (count is ignored for now)
		{
			string list_string;
			int line_number = 1;

			iterator = line_table.begin();
    		while( iterator != line_table.end() ) {
				list_string = (*iterator);
				if ( iterator == line_table.begin()+current_line )
					sprintf(line_buffer, "%8d* ", line_number++);
				else
					sprintf(line_buffer, "%8d  ", line_number++);
    	  		cout << line_buffer << list_string << endl;
    	  		++iterator;
			}
			return;
		}
	case 'C': // Show current line
		{
			sprintf(line_buffer, "%8d  ", current_line + 1);
			cout << line_buffer << line_table[current_line] << endl;
			return;
		}
	case 'T': // Move to top of source
		{
			current_line = -1;
			return;
		}
	case 'B': // Move to bottom of source
		{
			current_line = line_table.size();
			return;
		}
    }
}

// Main program entry point
int main(int argc, char *argv[]) {
	string command_line;
	line_editor editor;
	cout << "VEtLINe line editor - version 1.0" << endl << endl;
	while ( true ) {
		// Output the command prompt
		cout << "[Basic]:  ";
		getline(cin, command_line);
		editor.command(command_line);
	}
}



This concludes Part I of this tutorial. In part II, we will look at the basic language that we will interpret, and the parser that will be used to parse the user input in readiness for interpretation. Hold on to your seats ... this is going to be good!!!

Is This A Good Question/Topic? 5
  • +

Replies To: Writing An Interpreter

#2 vividexstance  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 665
  • View blog
  • Posts: 2,296
  • Joined: 31-December 10

Posted 04 February 2011 - 11:09 AM

I noticed a couple of errors in this line editor that you may want to change. In the switch statement, in case 'I'(insertion), while the editor is reading in strings, it checks for if(cin.eof()) break. When I was testing this out it would look something like this:
VEtLINe line editor - version 1.0

[Basic]:  i
       1 hello
       2 [Basic]:  


if you hit Ctrl-D to to stop inputting on the first line, it prints the [Basic]: prompt on the next insertion line. So I just added a "cout << endl;" in the if statement that checks for cin.eof(). Also, when I first ran the editor and it's looking for a command character, if I press Ctrl-D, it makes the loop in main go infinite and it keeps printing '[Basic]:' until I hit Ctrl-Z. How and where would you check for input like that to stop this from occurring?
Was This Post Helpful? 0
  • +
  • -

#3 Gebriel  Icon User is offline

  • New D.I.C Head

Reputation: 3
  • View blog
  • Posts: 23
  • Joined: 26-January 09

Posted 19 April 2011 - 11:48 PM

That is great, I will continue following your tutorials.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1