Solved

parsing if .. then and integer expressions

Posted on 2004-04-28
11
586 Views
Last Modified: 2010-05-18
Hello,

After many years of asp web programming i've decided to make a pascal script for development in the future, working on both delphi and web development makes you feel like using stoneage axes to break eggs when you begin asp programming after a long Delphi session.

I found one project (DWS delphi web script) available but 900kb source code was frightening, and i want something to be able to modify and tweak with, add plugins etc. so now have finally gotten around the creating of an isapi dll and making it available to IIS.

Does anyone know algorithims for parsing if <stament> then <something> and the i := ((((j+2)/(3+i))*3)/2)+2 situation

if statments like (((a=b) and (b>c)) and (c<d)) or (f=3) i guess this has to be done with recursive algorithm, currently i've managed simple if statments but havent really much clue about how to do this recursivly.

Kunfu Faresi
0
Comment
Question by:Kunfufaresi
  • 5
  • 2
  • 2
  • +1
11 Comments
 
LVL 4

Expert Comment

by:ceoworks
ID: 10944717
I suggest you to use regular expressions. There is some really usefull stuff in http://www.regular-expressions.info/ and if you will search for regular expressions in http://www.torry.net/pages.php?id=20, you may found some freeware Delphi components. Here is the topic which looks like the question you asked http://www.experts-exchange.com/Programming/Programming_Languages/Delphi/Q_20966039.html.

Regards,

Oktay Sancak
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10944835

Regular expressions just won't cut it I'm afraid. You are either going to need to write an LR Parser, or a recursive descent parser.

I have actually done a couple of parser/interpreter projects in Delphi, with different levels of complexity. Each project implements a recursive parser that compiles the code down to a byte type instruction record, and then a small interpreter set can be used to "execute" those instructions. If you want, I could post source for this tomorrow (provided no one else answers your question in the interim)

Regards,
Russell
0
 
LVL 3

Author Comment

by:Kunfufaresi
ID: 10945002
Thanks for the answers,

i've searched the net and been able to find both modules for if which returns true/false and math parser, however copying others code although is pragmatic :) does anyone know any texts explaining the proccess?
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10945519
Well....

Perform a search for +recursive+descent+parser on Google and start with the sites that provide explanations of parsing techniques, "top down" parsing, etc.

if you want it in a nutshell, then it comes down to this:

1.) Determining the syntax that your "language" will allow, and figure out what valid identifiers will be used (intristic functions, variable names, etc. .. for example "if", "then", "else" are all example of identifiers)

2.) Start off by building a function that is able to read, "tokenize", and process the text into a set of  record structures (or other suitable structure/object) that identifies the token.

eg:

x=5+1*2

// Psuedo tokens
token ident (x)
const int (5)
oper add
const int (1)
oper mult
const int (2)

3.) Build a function that is able to process a statement, where a statement in your case will be:

 assignment  : a:={expression}
 if                : if {expression} then {statements} else {statements}

4.) Build a function to  handle assignment evaluation. This will then require handling
(I recommend functions for these) for:

first:  =, <>, <, >, <=, >=
second: +, –, or, xor      
third: *, /, div, mod, and
fourth: numeric const, variable, (

where each calls the lower level to process first, before evaluating itself.

5.) Build a function to handle the  "if" processing. This brings up another topic...
Do you plan on a "process and execute" type of interpreter, or a process to byte code level (which ensures all the code is valid ahead of time) then execute? I prefer the second, as you can:
 
- detect issues in the code up front.
- implent a stack based interpreter on this very easily
- compile the source into a sort of psuedo code. eg

a = 5 + 1

then becomes

push 5
push 1
add (leaving 6 on the stack)
load a (with the value popped from the stack)

This makes the "if" statement processing easy as well. For example (I could show you code to do this ;-)

{if}
...expression leaves a value on the stack
{then}
jz 5 (jump 5 instructions forward if stack pops a zero)
... true statement pusedo asm (1)
... true statement pusedo asm (2)
... true statement pusedo asm (3)
jmp 4 (jump 4 instructions forward out of the "if...then..else" statement)
... false statement pusedo asm (1)
... false statement pusedo asm (2)
... false statement pusedo asm (3)
psuedo asm (out of the if ... then ... else at this point)


Anyways, hopefully you get the drift....

In regards to using others code, I agree to a point. Sometimes though, its easier to learn by first observing what others have done. At least I found it easier that way. Once I understood the techniques, then I was able to implement (and ofter improve upon) techniques gained from others. By the time I did my last 2 interpreters (one is an active scripting based interpreter, fully compatible with VBScript), I was writing all the code from the ground up.

Bottom line is, you need to gain an understanding of the techniques/methods involved first... and one of the best ways to learn is to evaluate what others have done. That also means finding source that is both simple and clean, because it doesn't help you at all if you can't understand it. That is all I was willing to offer...

Regards,
Russell
0
 
LVL 3

Expert Comment

by:MikProg
ID: 10946702
Step by step exlanation. We have a text string, stack structure, operators priority '()', '* /', '+ -', function that returns operand values by names, procedure that sets operand value by name. Symbol it is not character it is variable name or operator. Synatx is
    operand = { [(] operand [)] operator [(] operand [)] oper {variable}
i := j+2/3+i

evaluate expression function
get operand symbol
  get operand function
  [i] := j+2/3+i  is variable push to operand stack
Get symbol [:=] j+2/3+i    it must be operator push to operator stack
get operand symbol
  Get symbol [j]+2/3+i is variable Evaluate and push to operand stack
Get symbol [+]2/3+i  it must be operator . If priority of [+] < [:=] then stack top operator then execute stack top. Here it is not so push to operator stack.
get operand symbol
  Get symbol [2]/3+i is constant push to operand stack
Get symbol [/]3+i  it must be operator . If priority of [/] < [+] then stack top operator then execute stack top. Here it is not so push to operator stack.
get operand symbol
  Get symbol [3]+i is constant push to operand stack
Get symbol [+]i  it must be operator . If priority of [+] < [/] then stack top operator then execute stack top. Here it is. Execute stack top operator on stacktop operands 2/3 push result to stack then. push to operator stack
get operand symbol
  Get symbol [i] is variable Evaluate push to operand stack.
Get symbol []
 execute all stack operators
+
+
:= special operator Put top stack value to stack variable
     
if you introduce [(] [)] operators you need at [(] call evaluate expression function recursively and at [)] return result of expression between [(] [)] i.e [)] works like end of string but result stays on top of stack. Usual way [(] processed at getsymbol state i.e.  (expression) it is one of the operators. Similar way to introduce any operators and functions call (sqrt, exp, ...)

0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 26

Accepted Solution

by:
Russell Libby earned 500 total points
ID: 10953677
Well....

Believing that code speaks louder than words, I am posting source for an integer expression parser/stack based interpreter that fulfills the askers request.

It is a little large (40K, 1400 loc), but it is nowhere near 900KB of source ;-). Hopefully this code has simplified (and made easy to read) some of the things that goes into creating a small scripting language, and how parsing up front, before executing the code can make your life easier:

eg:
1.) You know before execution if the source text is even valid (can compile)
2.) Absolute and conditional jumps are worked out ahead of time.
3.) Variable storage is worked out ahead of time
4.) The actual execution routine for the interpreter only requires 125 lines of code.
5.) The code execution routine is NON-RECURSIVE, combine this with the small amount of code required to implement the interpreter,and you get very fast execution times. For example, on my system, I am able to execute the following code:

i :=  ((((j+2)/(3+i))*3)/2)+2
if (((a=b) and (b>c)) and (c<d)) or (f=3) then
  a:=100
  b:=2
else
 c:=20
endif

about 370,000 times / second. Not too bad for an interpreter....

-----

Regarding the recursive math expression parsing, if you follow through the following routines in the TIntCompile class, you should have a pretty good understanding of what goes into it:

ParseLevel1
ParseLevel2
ParseLevel3
ParseLevel4

------------------

Anyways, I tried to limit the amount the code, yet still provide you with something useful to follow. (Please also consider that appx 30% of the total code is comments, which should help in the understanding).

I finish off with a short example of how the classes can be used:

procedure TForm1.Button1Click(Sender: TObject);
var  Compiler:      TIntCompile;
     Interpreter:   TIntInterpreter;
     dwStart:       LongWord;
     dwEnd:         LongWord;
     dwIndex:       Integer;
     Source:        String;
const
  NL                =  #13#10;
begin

  Source:='i :=  ((((j+2)/(3+i))*3)/2)+2'+NL+
          'if (((a=b) and (b>c)) and (c<d)) or (f=3) then'+NL+
          '   a:=100'+NL+'   b:=2'+NL+'else'+NL+'   c:=20'+NL+'endif';

  // Create an instance of the compiler
  Compiler:=TIntCompile.Create(Source);

  // Create an instance of the interpreter based on the compiler.
  // Note: After the interpreter has been created, the compiler object
  // can be safely destroyed if desired.
  Interpreter:=TIntInterpreter.Create(Compiler);

  // Resource protection
  try

     // You are able to set variables before running
     // the execute procedure. Also, the variables are not case sensitive and do
     // not have to be single letters, eg: MyTestVariable would be valid
     Interpreter['j']:=30;
     Interpreter['i']:=20;
     Interpreter['C']:=1;
     Interpreter['f']:=2;

     // Excute the code
     Interpreter.Execute;

     // Evaluate any results after the execution if you wish
     ShowMessage(Format('variable %s = %d', ['a', Interpreter['a']]));
     ShowMessage(Format('variable %s = %d', ['b', Interpreter['b']]));
     ShowMessage(Format('variable %s = %d', ['c', Interpreter['c']]));
     ShowMessage(Format('variable %s = %d', ['i', Interpreter['i']]));

     // You can also reset the intepreter, if you wanted, and can
     // rerun the code again. (a Reset sets all variables to zero.)
     dwStart:=GetTickCount;
     for dwIndex:=0 to 100000 do
     begin
        Interpreter.Reset;
        Interpreter.Execute;
     end;
     dwEnd:=GetTickCount-dwStart;
     ShowMessage(IntToStr(dwEnd));

  finally
     // Free the objects
     Compiler.Free;
     Interpreter.Free;
  end;

end;


Hope this helps, and please ask if you have any questions .
Russell

------------------------

unit IntEval;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  INTEVAL
//   Classes     :  TIntExpr
//   Date        :  04.29.2004
//
//   Description :  Integer expression parser. All data is treated as integer
//                  type, and support for common functions such as abs, min,
//                  max, not, mod, etc have also been included.
//
//
//   Valid statements
//   ----------------
//
//   {variable} := {numeric expression}
//   if {expression} then {statements} [else {statements}] endif
//   show {numeric expression}
//
//   Special punctuation
//   ------------------------
//
//   ( ) ,
//
//   Operators
//   ---------
//
//   :=          Assignment
//   =           Equals
//   <=          Less than equals
//   <           Less than
//   >=          Greater than equals
//   >           Greater than
//   <>          Not equals
//   +           Add
//   -           Subtraction or negation
//   /           Integer division (can also use "div")
//   *           Multiply
//   mod         Modulus
//   or          Disjunction
//   and         Conjunction
//   xor         Exclusive disjunction
//   not         Negation
//   shl         Shift left
//   shr         Shift right
//
//   Functions
//   ---------
//
//   abs(x)      Absoulte of number
//   min(x, y)   Returns lower of the two values
//   max(x, y)   Returns higher of the two values
//
////////////////////////////////////////////////////////////////////////////////
interface

uses
  Windows, SysUtils, Classes;

////////////////////////////////////////////////////////////////////////////////
//   Assembly opcodes for expression evaluation
////////////////////////////////////////////////////////////////////////////////
const
  // No operation
  OP_NOP            =  00;
  // Operations requiring 2 numbers
  OP_EQ             =  01;
  OP_LEQ            =  02;
  OP_GEQ            =  03;
  OP_LT             =  04;
  OP_GT             =  05;
  OP_NEQ            =  06;
  OP_ADD            =  07;
  OP_SUB            =  08;
  OP_DIV            =  09;
  OP_MULT           =  10;
  OP_MOD            =  11;
  OP_OR             =  12;
  OP_XOR            =  13;
  OP_AND            =  14;
  OP_SHL            =  15;
  OP_SHR            =  16;
  OP_MIN            =  17;
  OP_MAX            =  18;
  // Operations requiring 1 number
  OP_NEG            =  19;
  OP_NOT            =  20;
  OP_ABS            =  21;
  // Jump instructions
  OP_JZ             =  22;
  OP_JMP            =  23;
  // Ouput instruction
  OP_SHOW           =  24;
  // Save/Load operations
  OP_SVN            =  25;
  OP_LVN            =  26;
  OP_LCN            =  27;
  // End of execution operation
  OP_END            =  28;

////////////////////////////////////////////////////////////////////////////////
//   Min/Max valid assembly code constants
////////////////////////////////////////////////////////////////////////////////
const
  MIN_OP            =  OP_NOP;
  MAX_OP            =  OP_LCN;

////////////////////////////////////////////////////////////////////////////////
//   Token constants
////////////////////////////////////////////////////////////////////////////////
type
  TParserToken      =  (ptAssign,  ptEq,    ptLeq,   ptLt,    ptGeq,   ptGt,
                        ptNeq,     ptAdd,   ptSub,   ptDiv,   ptMult,  ptMod,
                        ptOr,      ptAnd,   ptXor,   ptNot,   ptShl,   ptShr,
                        ptAbs,     ptVar,   ptLp,    ptRp,    ptNum,   ptMin,
                        ptMax,     ptIf,    ptThen,  ptElse,  ptEndIf, ptComma,
                        ptShow,    ptEnd);

////////////////////////////////////////////////////////////////////////////////
//   Custom data types
////////////////////////////////////////////////////////////////////////////////
type
  TParseToken       =  packed record
     ParseToken:    TParserToken;
     case Integer of
        0  :  (Index: Integer);
        1  :  (Value: Integer);
  end;
  PAsm              =  ^TAsm;
  TAsm              =  packed record
     case Integer of
        0  :        (dwInstr: Integer);
        1  :        (wOpCode: Word; wIndex: Word);
  end;

////////////////////////////////////////////////////////////////////////////////
//   Exception classs for compiling/executing
////////////////////////////////////////////////////////////////////////////////
type
  EIntEvalCompile   =  class(Exception);
  EIntEvalExecute   =  class(Exception);

////////////////////////////////////////////////////////////////////////////////
//   Resource strings for exceptions
////////////////////////////////////////////////////////////////////////////////
resourcestring
  SExecute          =  'Execution halted with: %s';
  SInvalidInstr     =  'Invalid instruction encountered';
  SNullCompiler     =  'Null TIntCompile object passed to interpreter';
  SUnexpected       =  'Unexpected character encountered';
  SInvalidIdent     =  'Invalid identifier encountered';
  SInvalidNum       =  'Invalid number';
  SAsmOverflow      =  'Too many operations for a single expression';
  SNumOverflow      =  'Too many constant numbers for a single expression';
  SCloseParen       =  'Closing parentheses missing';
  SComma            =  'Comma missing';
  SOpenParen        =  'Open parentheses missing';
  SSyntaxError      =  'Syntax error in expression';
  SUnderflow        =  'Stack underflow';
  SOverflow         =  'Stack overflow';

////////////////////////////////////////////////////////////////////////////////
//   Assembly code instruction list storage
////////////////////////////////////////////////////////////////////////////////
type
  TIntAsmStorage    =  class(TObject)
  private
     // Private declarations
     FAsmCode:      TList;
  protected
     // Protected declarations
     procedure      SetItems(Index: Integer; Value: TAsm);
     function       GetCount: Integer;
     function       GetItems(Index: Integer): TAsm;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     procedure      Clear;
     function       Add(OpCode: Word = OP_NOP; Index: Word = 0): Integer;
     property       Items[Index: Integer]: TAsm read GetItems write SetItems; default;
     property       Count: Integer read GetCount;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Variable storage for compiling script
////////////////////////////////////////////////////////////////////////////////
type
  TIntStorage       =  class(TObject)
  private
     // Private declarations
     FStorage:      TStringList;
  protected
     // Protected declarations
     function       GetVarName(Index: Integer): String;
     function       GetVarCount: Integer;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     procedure      Clear;
     function       Add(VarName: String): Integer;
     function       IndexOf(VarName: String): Integer;
     property       VarName[Index: Integer]: String read GetVarName;
     property       VarCount: Integer read GetVarCount;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Stack object for run time execution
////////////////////////////////////////////////////////////////////////////////
type
  TIntStack         =  class(TObject)
  private
     // Private declarations
     FStack:        TList;
  protected
     // Protected declarations
     function       GetCount: Integer;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     procedure      Clear;
     procedure      Push(Value: Boolean); overload;
     procedure      Push(Value: Integer); overload;
     function       Pop: Integer;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Compiler object for compiling text into runtime instruction code
////////////////////////////////////////////////////////////////////////////////
type
  TIntCompile       =  class(TObject)
  private
     // Private declarations
     FParsed:       TParseToken;
     FSource:       PChar;
     FParse:        PChar;
     FCompiled:     Boolean;
  protected
     // Make available to classes in this unit
     FVariable:     TIntStorage;
     FAssembly:     TIntAsmStorage;
     FConsts:       TList;
     // Protected declarations
     procedure      Parse;
     procedure      ParseNext;
     procedure      ParseIf;
     procedure      ParseLevel1;
     procedure      ParseLevel2;
     procedure      ParseLevel3;
     procedure      ParseLevel4;
     procedure      ParseShow;
     procedure      SetCompiled(Value: Boolean);
     procedure      SetSource(Value: String);
     function       GenOpCode(OpCode: Word; Index: Word): Integer;
     function       GetSource: String;
     function       StoreConst(Value: Integer): Word;
  public
     // Public declarations
     constructor    Create(SourceText: String);
     destructor     Destroy; override;
     property       SourceText: String read GetSource write SetSource;
     property       Compiled: Boolean read FCompiled write SetCompiled;
  end;

////////////////////////////////////////////////////////////////////////////////
//   Interpreter object for running the compiled code
////////////////////////////////////////////////////////////////////////////////
type
  TIntInterpreter   =  class(TObject)
  private
     // Private declarations
     FStack:        TIntStack;
     FVars:         TStringList;
     FVarValues:    TList;
     FConsts:       TList;
     FAsm:          TList;
  protected
     // Protected declarations
     procedure      Load(Compiler: TIntCompile);
     procedure      SetValues(Variable: String; Value: Integer);
     function       GetValues(Variable: String): Integer;
     function       Max(Value1, Value2: Integer): Integer;
     function       Min(Value1, Value2: Integer): Integer;
  public
     // Public declaration
     constructor    Create(Compiler: TIntCompile);
     destructor     Destroy; override;
     procedure      Execute;
     procedure      Reset;
     property       Values[Variable: String]: Integer read GetValues write SetValues; default;
  end;

implementation

////////////////////////////////////////////////////////////////////////////////
//   Protected Variables
////////////////////////////////////////////////////////////////////////////////
var
  IdentList:        TStringList;

////////////////////////////////////////////////////////////////////////////////
//   TIntInterpreter
////////////////////////////////////////////////////////////////////////////////
procedure TIntInterpreter.Load(Compiler: TIntCompile);
var  OpAsm:         TAsm;
     dwIndex:       Integer;
begin

  // Check valid object
  if not(Assigned(Compiler)) then raise EIntEvalExecute.CreateRes(@SNullCompiler);

  // Make sure the source text has been compiled
  Compiler.Compiled:=True;

  // Transfer the contents of the compiled state into the interpreter
  for dwIndex:=0 to Pred(Compiler.FVariable.VarCount) do
  begin
     // Load the variable name list (used for exposing variable values to the
     // outside world. The interpreter itself only deals with the values list
     // when executing.
     FVars.Add(Compiler.FVariable.VarName[dwIndex]);
     FVarValues.Add(nil);
  end;

  // Transfer the constant values
  for dwIndex:=0 to Pred(Compiler.FConsts.Count) do FConsts.Add(Compiler.FConsts[dwIndex]);

  // Transfer the assembly code
  for dwIndex:=0 to Pred(Compiler.FAssembly.Count) do
  begin
     // Get the instruction
     OpAsm:=Compiler.FAssembly[dwIndex];
     // Add to list
     FAsm.Add(Pointer(OpAsm.dwInstr));
  end;

end;

procedure TIntInterpreter.SetValues(Variable: String; Value: Integer);
var  dwIndex:       Integer;
begin

  // Lookup the variable name. If we find it, set the value for
  // the variable, otherwise add a new variable. Note: This doesn't do
  // much good if the variable is not used in the script, but we allow
  // for it anyways.
  dwIndex:=FVars.IndexOf(Variable);

  // Was the item found?
  if (dwIndex < 0) then
  begin
     FVars.Add(Variable);
     FVarValues.Add(nil);
  end
  else
     // Set the new value for the variable
     FVarValues[dwIndex]:=Pointer(Value);

end;

function TIntInterpreter.Max(Value1, Value2: Integer): Integer;
begin

  // Return the max value
  if (Value1 > Value2) then
     result:=Value1
  else
     result:=Value2;

end;

function TIntInterpreter.Min(Value1, Value2: Integer): Integer;
begin

  // Return the min value
  if (Value1 < Value2) then
     result:=Value1
  else
     result:=Value2;

end;

function TIntInterpreter.GetValues(Variable: String): Integer;
var  dwIndex:       Integer;
begin

  // Lookup the variable name. If we find it, return the value for
  // the variable, otherwise return 0 (the variable is undefined)
  dwIndex:=FVars.IndexOf(Variable);

  // Was the item found?
  if (dwIndex < 0) then
     // Return default
     result:=0
  else
     // Return the integer value for the variable
     result:=Integer(FVarValues[dwIndex]);

end;

constructor TIntInterpreter.Create(Compiler: TIntCompile);
begin

  // Perform inherited
  inherited Create;

  // Set starting defaults
  FStack:=TIntStack.Create;
  FVars:=TStringList.Create;
  FVarValues:=TList.Create;
  FConsts:=TList.Create;
  FAsm:=TList.Create;

  // Load from the passed in compiler object
  Load(Compiler);

end;

destructor TIntInterpreter.Destroy;
begin

  // Cleanup
  FStack.Free;
  FVars.Free;
  FVarValues.Free;
  FConsts.Free;
  FAsm.Free;

  // Perform inherited
  inherited Destroy;

end;

procedure TIntInterpreter.Execute;
var  OpAsm:         TAsm;
     dwIP:          Integer;
     dwVals:        Array [0..1] of Integer;
begin

  // Reset the execution state
  FStack.Clear;

  // Set starting instruction pointer
  dwIP:=0;

  // Get the first instruction code
  OpAsm.dwInstr:=Integer(FAsm[dwIP]);

  // Iterate until we hit the OP_END
  try
     while (OpAsm.wOpCode <> OP_END) do
     begin
        // Handle the instruction
        case OpAsm.wOpCode of
           // Do nothing instruction
           OP_NOP            :  Inc(dwIP);
           // 2 number operation
           OP_EQ..OP_MAX     :
           begin
              // Pop 2 values from the stack
              dwVals[1]:=FStack.Pop;
              dwVals[0]:=FStack.Pop;
              // Perform the operation
              case OpAsm.wOpCode of
                 OP_EQ    :  FStack.Push(dwVals[0] = dwVals[1]);
                 OP_LEQ   :  FStack.Push(dwVals[0] <= dwVals[1]);
                 OP_GEQ   :  FStack.Push(dwVals[0] >= dwVals[1]);
                 OP_LT    :  FStack.Push(dwVals[0] < dwVals[1]);
                 OP_GT    :  FStack.Push(dwVals[0] > dwVals[1]);
                 OP_NEQ   :  FStack.Push(dwVals[0] <> dwVals[1]);
                 OP_ADD   :  FStack.Push(dwVals[0] + dwVals[1]);
                 OP_SUB   :  FStack.Push(dwVals[0] - dwVals[1]);
                 OP_DIV   :  FStack.Push(dwVals[0] div dwVals[1]);
                 OP_MULT  :  FStack.Push(dwVals[0] * dwVals[1]);
                 OP_MOD   :  FStack.Push(dwVals[0] mod dwVals[1]);
                 OP_OR    :  FStack.Push(dwVals[0] or dwVals[1]);
                 OP_XOR   :  FStack.Push(dwVals[0] xor dwVals[1]);
                 OP_AND   :  FStack.Push(dwVals[0] and dwVals[1]);
                 OP_SHL   :  FStack.Push(dwVals[0] shl dwVals[1]);
                 OP_SHR   :  FStack.Push(dwVals[0] shr dwVals[1]);
                 OP_MIN   :  FStack.Push(Min(dwVals[0], dwVals[1]));
                 OP_MAX   :  FStack.Push(Max(dwVals[0], dwVals[1]));
              end;
              // Increment the instruction pointer
              Inc(dwIP);
           end;
           // 1 number operation
           OP_NEG..OP_ABS    :
           begin
              case OpAsm.wOpCode of
                 OP_NEG      :  FStack.Push(FStack.Pop * -1);
                 OP_NOT      :  FStack.Push(not(FStack.Pop));
                 OP_ABS      :  FStack.Push(Abs(FStack.Pop));
              end;
              // Increment the instruction pointer
              Inc(dwIP);
           end;
           // Jump if zero
           OP_JZ             :
           begin
              // Check stack value
              if (FStack.Pop = 0) then
                 // Increment the instruction pointer by index
                 Inc(dwIP, OpAsm.wIndex)
              else
                 // Next instruction
                 Inc(dwIP);
           end;
           // Absolute jump
           OP_JMP            :  Inc(dwIP, OpAsm.wIndex);
           // Show the value on the stack
           OP_SHOW           :
           begin
              // Check console mode. If running console, we will writeln the value,
              // otherwise a message box will be displayed with the value
              if IsConsole then
                 WriteLn(FStack.Pop)
              else
                 MessageBox(0, PChar(IntToStr(FStack.Pop)), 'Show', MB_OK+MB_ICONINFORMATION);
              // Increment the instruction pointer
              Inc(dwIP);
           end;
           // Save to variable
           OP_SVN            :
           begin
              // Save variable value from the stack
              FVarValues[OpAsm.wIndex]:=Pointer(FStack.Pop);
              // Increment the instruction pointer
              Inc(dwIP);
           end;
           // Load variable
           OP_LVN            :
           begin
              // Load variable value onto the stack
              FStack.Push(Integer(FVarValues[OpAsm.wIndex]));
              // Increment the instruction pointer
              Inc(dwIP);
           end;
           // Load constant
           OP_LCN            :
           begin
              // Load constant value onto the stack
              FStack.Push(Integer(FConsts[OpAsm.wIndex]));
              // Increment the instruction pointer
              Inc(dwIP);
           end;
        else
           // Invalid instruction
           raise EIntEvalExecute.CreateRes(@SInvalidInstr);
        end;
        // Get the next instruction
        OpAsm.dwInstr:=Integer(FAsm[dwIP]);
     end;
  except
     // Raise correct typed exception
     on E: Exception do raise EIntEvalExecute.CreateResFmt(@SExecute, [E.Message]);
  end;

end;

procedure TIntInterpreter.Reset;
var  dwIndex:    Integer;
begin

  // Set all variable values to zero
  for dwIndex:=0 to Pred(FVarValues.Count) do FVarValues[dwIndex]:=nil;

end;

////////////////////////////////////////////////////////////////////////////////
//   TIntCompile
////////////////////////////////////////////////////////////////////////////////
function TIntCompile.GetSource: String;
begin

  // Return current source string
  result:=FSource;

end;

procedure TIntCompile.SetSource(Value: String);
begin

  // Clear the current compiled state
  SetCompiled(False);

  // Free the source string
  FreeMem(FSource);

  // Allocate a new source string
  FSource:=AllocMem(Succ(Length(Value)));
  StrPCopy(FSource, Value);

  // Set the parse point
  FParse:=FSource;

end;

procedure TIntCompile.SetCompiled(Value: Boolean);
begin

  // Check current state
  if (Value <> FCompiled) then
  begin
     // Update state
     FCompiled:=Value;
     // Should we be compiling?
     if FCompiled  then
     begin
        try
           // Seed the first token
           ParseNext;
           // Attempt to parse
           Parse
        except
           // Failed to parse
           SetCompiled(False);
           // Re-raise the exception
           raise;
        end;
     end
     else
     begin
        // Clear everything
        ZeroMemory(@FParsed, SizeOf(TParseToken));
        FVariable.Clear;
        FAssembly.Clear;
        FConsts.Clear;
        FParse:=FSource;
     end;
  end;

end;

procedure TIntCompile.ParseLevel4;
var  ptToken:       TParserToken;
begin

  // Low level expression evaluator
  case FParsed.ParseToken of
     // Variable load
     ptVar       :
     begin
        // Generate code to load the variable
        GenOpCode(OP_LVN, FParsed.Index);
        // Seed the next token
        ParseNext;
     end;
     // Constant number load
     ptNum       :
     begin
        // Store the number in the static data table
        FParsed.Index:=StoreConst(FParsed.Value);
        // Generate code to push constant number stored in code
        GenOpCode(OP_LCN, FParsed.Index);
        // Seed the next token
        ParseNext;
     end;
     // Left paren
     ptLp        :
     begin
        // Seed the next token
        ParseNext;
        // Build the sub expression
        ParseLevel1;
        // Better be sitting at a right paren
        if (FParsed.ParseToken <> ptRp) then raise EIntEvalCompile.CreateRes(@SCloseParen);
        // Seed the next token
        ParseNext;
     end;
     // Single parameter functions
     ptNot,
     ptAbs       :
     begin
        // Hold the current token identifier
        ptToken:=FParsed.ParseToken;
        // Seed next token
        ParseNext;
        // Check open parens
        if (FParsed.ParseToken <> ptLp) then raise EIntEvalCompile.CreateRes(@SOpenParen);
        // Seed next token
        ParseNext;
        // Build numeric expression
        ParseLevel1;
        // Better be sitting at a right paren
        if (FParsed.ParseToken <> ptRp) then raise EIntEvalCompile.CreateRes(@SCloseParen);
        // Seed the next token
        ParseNext;
        // Generate code
        case ptToken of
           ptNot    :  GenOpCode(OP_NOT, 0);
           ptAbs    :  GenOpCode(OP_ABS, 0);
        end;
     end;
     // Two parameter functions
     ptMin,
     ptMax       :
     begin
        // Hold the current token identifier
        ptToken:=FParsed.ParseToken;
        // Seed next token
        ParseNext;
        // Check open parens
        if (FParsed.ParseToken <> ptLp) then raise EIntEvalCompile.CreateRes(@SOpenParen);
        // Seed next token
        ParseNext;
        // Build numeric expression
        ParseLevel1;
        // Better be sitting at a comma
        if (FParsed.ParseToken <> ptComma) then raise EIntEvalCompile.CreateRes(@SComma);
        ParseNext;
        // Build numeric expression
        ParseLevel1;
        // Better be sitting at a right paren
        if (FParsed.ParseToken <> ptRp) then raise EIntEvalCompile.CreateRes(@SCloseParen);
        // Seed the next token
        ParseNext;
        // Generate code
        case ptToken of
           ptMin    :  GenOpCode(OP_MIN, 0);
           ptMax    :  GenOpCode(OP_MAX, 0);
        end;
     end;
  else
     // Syntax error
     raise EIntEvalCompile.CreateRes(@SSyntaxError);
  end;

end;

procedure TIntCompile.ParseLevel3;
var  ParseToken:    TParserToken;
begin

  // Build factor
  ParseLevel4;

  // Handle /, *, mod, shr, shr, and
  while (FParsed.ParseToken in [ptMult, ptDiv, ptMod, ptShr, ptShl, ptAnd]) do
  begin
     // Hold the identifier
     ParseToken:=FParsed.ParseToken;
     // Seed the next token
     ParseNext;
     // Build factor
     ParseLevel4;
     // Generate code
     case ParseToken of
        ptMult   :  GenOpCode(OP_MULT, 0);
        ptDiv    :  GenOpCode(OP_DIV, 0);
        ptMod    :  GenOpCode(OP_MOD, 0);
        ptShl    :  GenOpCode(OP_SHL, 0);
        ptShr    :  GenOpCode(OP_SHR, 0);
        ptAnd    :  GenOpCode(OP_AND, 0);
     end;
  end;

end;

procedure TIntCompile.ParseLevel2;
var  ParseToken:    TParserToken;
begin

  // Handle + and - numbers
  if (FParsed.ParseToken in [ptAdd, ptSub]) then
  begin
     // Hold the identifier
     ParseToken:=FParsed.ParseToken;
     // Seed the next token
     ParseNext;
     // Build term
     ParseLevel3;
     // Generate code for negate
     if (ParseToken = ptSub) then GenOpCode(OP_NEG, 0);
  end
  else
     // Build term
     ParseLevel3;

  // Handle +, -, or, xor
  while (FParsed.ParseToken in [ptAdd, ptSub, ptOr, ptXor]) do
  begin
     // Hold the identifier
     ParseToken:=FParsed.ParseToken;
     // Seed the next token
     ParseNext;
     // Build next term
     ParseLevel3;
     // Generate code
     case ParseToken of
        ptAdd    :  GenOpCode(OP_ADD, 0);
        ptSub   :  GenOpCode(OP_SUB, 0);
        ptOr    :  GenOpCode(OP_OR, 0);
        ptXor   :  GenOpCode(OP_XOR, 0);
     end;
  end;

end;

procedure TIntCompile.ParseLevel1;
var  ParseToken:   TParserToken;
begin

  // Build a simple expression
  ParseLevel2;

  // What kind of token?
  if (FParsed.ParseToken in [ptEq, ptNeq, ptLeq, ptLt, ptGeq, ptGt]) then
  begin
     // Save the identifier
     ParseToken:=FParsed.ParseToken;
     // Seed the next token
     ParseNext;
     // Build another simple expression
     ParseLevel2;
     // Generate asm code
     case ParseToken of
        ptEq     :  GenOpCode(OP_EQ, 0);
        ptLt     :  GenOpCode(OP_LT, 0);
        ptLeq    :  GenOpCode(OP_LEQ, 0);
        ptGt     :  GenOpCode(OP_GT, 0);
        ptGeq    :  GenOpCode(OP_GEQ, 0);
        ptNeq    :  GenOpCode(OP_NEQ, 0);
     end;
  end;

end;

procedure TIntCompile.ParseShow;
begin

  // Seed the next token
  ParseNext;

  // Parse the expression
  ParseLevel1;

  // Add the show instruction
  GenOpCode(OP_SHOW, 0);

end;

procedure TIntCompile.ParseIf;
var  OpAsm:         TAsm;
     dwIndex:       Integer;
     dwHold:        Integer;
     dwJump:        Integer;
begin

  // Skip the "if"
  ParseNext;

  // Build the expression
  ParseLevel1;

  // Skip the "then"
  if (FParsed.ParseToken <> ptThen) then raise EIntEvalCompile.CreateRes(@SSyntaxError);
  ParseNext;

  // Hold a place for the jump not zero (we will need to fix this up)
  dwHold:=GenOpCode(OP_JZ, 0);

  // Parse until we hit one of [ptElse, ptEndIf]
  while not(FParsed.ParseToken in [ptElse, ptEndIf]) do
  begin
     // Handle the statement
     case FParsed.ParseToken of
        // Assignment statement
        ptVar    :
        begin
           // Save the variable index
           dwIndex:=FParsed.Index;
           // Get the next token, which must be an assignment
           ParseNext;
           if (FParsed.ParseToken <> ptAssign) then raise EIntEvalCompile.CreateRes(@SSyntaxError);
           // Seed the next token
           ParseNext;
           // Build the numeric expression
           ParseLevel1;
           // Generate the save instruction
           GenOpCode(OP_SVN, dwIndex);
        end;
        // If ... then ... statement
        ptIf     :  ParseIf;
        // Show ...
        ptShow   :  ParseShow;
     else
        // Handle invalid expressions or an unexpected end of text stream
        raise EIntEvalCompile.CreateRes(@SSyntaxError);
     end;
  end;

  // Parse the "Else"
  if (FParsed.ParseToken = ptElse) then
  begin
     // Jump needs to be added
     dwJump:=GenOpCode(OP_JMP, 0);
     // Need to finish the false jump
     dwIndex:=GenOpCode(OP_NOP, 0);
     // Compile the full conditional jump
     OpAsm.wOpCode:=OP_JZ;
     OpAsm.wIndex:=dwIndex-dwHold;
     // Update the existing instruction
     FAssembly[dwHold]:=OpAsm;
     // Jump is now the one that will need fixing
     dwHold:=dwJump;
     // Seed next token
     ParseNext;
     // Parse until we hit one ptEndIf
     while (FParsed.ParseToken <> ptEndIf) do
     begin
        // Handle the statement
        case FParsed.ParseToken of
           // Assignment statement
           ptVar    :
           begin
              // Save the variable index
              dwIndex:=FParsed.Index;
              // Get the next token, which must be an assignment
              ParseNext;
              if (FParsed.ParseToken <> ptAssign) then raise EIntEvalCompile.CreateRes(@SSyntaxError);
              // Seed the next token
              ParseNext;
              // Build the numeric expression
              ParseLevel1;
              // Generate the save instruction
              GenOpCode(OP_SVN, dwIndex);
           end;
           // If ... then ... statement
           ptIf     :  ParseIf;
           // Show ...
           ptShow   :  ParseShow;
        else
           // Handle invalid expressions or an unexpected end of text stream
           raise EIntEvalCompile.CreateRes(@SSyntaxError);
        end;
     end;
  end;

  // Must be sitting at an endif
  if (FParsed.ParseToken <> ptEndIf) then raise EIntEvalCompile.CreateRes(@SSyntaxError);
  ParseNext;

  // Add a no operation
  dwIndex:=GenOpCode(OP_NOP, 0);

  // Get the instruction at index, which may either be a JMP or JZ
  OpAsm:=FAssembly[dwHold];

  // Update the index for the jump
  OpAsm.wIndex:=dwIndex-dwHold;

  // Update the instruction
  FAssembly[dwHold]:=OpAsm;

end;

procedure TIntCompile.Parse;
var  dwIndex:    Integer;
begin

  // While not ptEnd, keep parsing the
  while (FParsed.ParseToken <> ptEnd) do
  begin
     // Handle the statement
     case FParsed.ParseToken of
        // Assignment statement
        ptVar    :
        begin
           // Save the variable index
           dwIndex:=FParsed.Index;
           // Get the next token, which must be an assignment
           ParseNext;
           if (FParsed.ParseToken <> ptAssign) then raise EIntEvalCompile.CreateRes(@SSyntaxError);
           // Seed the next token
           ParseNext;
           // Build the numeric expression
           ParseLevel1;
           // Generate the save instruction
           GenOpCode(OP_SVN, dwIndex);
        end;
        // If ... then ... statement
        ptIf     :  ParseIf;
        // Show ...
        ptShow   :  ParseShow;
     else
        // Failed to parse
        raise EIntEvalCompile.CreateRes(@SSyntaxError);
     end;
  end;

  // Generate the final opcode
  GenOpCode(OP_END, 0);

end;

procedure TIntCompile.ParseNext;
var  lpszIdent:  PChar;
     dwTemp:     Integer;
     cMark:      Char;
begin

  // Clear the current token
  ZeroMemory(@FParsed, SizeOf(TParseToken));

  // Check current parse point
  if (FParse = nil) then
     // End of expression
     FParsed.ParseToken:=ptEnd
  else
  begin
     // Skip white space
     while (FParse^ in [#9, #10, #13, #32]) do Inc(FParse);
     // Get next token
     case FParse^ of
        // End of expression
        #0       :  FParsed.ParseToken:=ptEnd;
        // Left paren
        '('      :  FParsed.ParseToken:=ptLp;
        // Right paren
        ')'      :  FParsed.ParseToken:=ptRp;
        // Multiply
        '*'      :  FParsed.ParseToken:=ptMult;
        // Add
        '+'      :  FParsed.ParseToken:=ptAdd;
        // Comma :
        ','      :  FParsed.ParseToken:=ptComma;
        // Subtract
        '-'      :  FParsed.ParseToken:=ptSub;
        // Optional for div expression
        '/'      :  FParsed.ParseToken:=ptDiv;
        // Numeric number (numbers cannot start with a .)
        '0'..'9' :
        begin
           // Hold the identifier start
           lpszIdent:=FParse;
           // Find the end of the number
           while (FParse^ in ['0'..'9']) do Inc(FParse);
           // Hold the current char and mark
           cMark:=FParse^;
           FParse^:=#0;
           // Convert to number
           FParsed.ParseToken:=ptNum;
           Val(lpszIdent, FParsed.Value, dwTemp);
           // Check conversion
           if (dwTemp > 0) then raise EIntEvalCompile.CreateRes(@SInvalidNum);
           // Reset the mark point and skip back one in the parse stream
           FParse^:=cMark;
           Dec(FParse);
        end;
        // :=
        ':'      :
        begin
           Inc(FParse);
           if (FParse^ <> '=') then raise EIntEvalCompile.CreateRes(@SSyntaxError);
           FParsed.ParseToken:=ptAssign;
        end;
        // Less than
        '<'      :
        begin
           // Move to next char in stream
           Inc(FParse);
           // Check next character
           case FParse^ of
              // Less than equals
              '='   :  FParsed.ParseToken:=ptLeq;
              // Not equals
              '>'   :  FParsed.ParseToken:=ptNeq;
           else
              // Less than
              FParsed.ParseToken:=ptLt;
              Dec(FParse);
           end;
        end;
        // Equals
        '='      :  FParsed.ParseToken:=ptEq;
        // Greater than
        '>'      :
        begin
           // Move to next char in stream
           Inc(FParse);
           // Check next character
           if (FParse^  = '=') then
              // Greater than equals
              FParsed.ParseToken:=ptGeq
           else
           begin
              // Greater than
              FParsed.ParseToken:=ptGt;
              Dec(FParse);
           end;
        end;
        // Identifier
        'A'..'Z',
        'a'..'z' :
        begin
           // Hold the identifier start
           lpszIdent:=FParse;
           // Find the end of the identifier
           while (FParse^ in ['A'..'Z', 'a'..'z']) do Inc(FParse);
           // Hold the current char and mark
           cMark:=FParse^;
           FParse^:=#0;
           // See if this is a valid identifier. If not, it will be a variable
           if IdentList.Find(lpszIdent, dwTemp) then
              // Get the identifier
              FParsed.ParseToken:=TParserToken(IdentList.Objects[dwTemp])
           else
           begin
              // Going to be a variable
              FParsed.ParseToken:=ptVar;
              FParsed.Index:=FVariable.Add(lpszIdent);
           end;
           // Reset the mark point and skip back one in the parse stream
           FParse^:=cMark;
           Dec(FParse);
        end;
     else
        // Invalid chararcter
        raise EIntEvalCompile.CreateRes(@SUnexpected);
     end;
     // Push next in parse stream
     Inc(FParse);
  end;

end;

function TIntCompile.StoreConst(Value: Integer): Word;
begin

  // Store the constant number
  result:=FConsts.Add(Pointer(Value));

end;

function TIntCompile.GenOpCode(OpCode: Word; Index: Word): Integer;
begin

  // Add the next assembly instruction
  result:=FAssembly.Add(OpCode, Index);

end;

constructor TIntCompile.Create(SourceText: String);
begin

  // Perform inherited
  inherited Create;

  // Set starting defaults
  ZeroMemory(@FParsed, SizeOf(TParseToken));
  FVariable:=TIntStorage.Create;
  FAssembly:=TIntAsmStorage.Create;
  FConsts:=TList.Create;
  FSource:=AllocMem(Succ(Length(SourceText)));
  StrPCopy(FSource, SourceText);
  FParse:=FSource;
  FCompiled:=False;

end;

destructor TIntCompile.Destroy;
begin

  // Clean up
  FVariable.Free;
  FAssembly.Free;
  FConsts.Free;
  FreeMem(FSource);

  // Perform inherited
  inherited Destroy;

end;

////////////////////////////////////////////////////////////////////////////////
//   TIntAsmStorage
////////////////////////////////////////////////////////////////////////////////
procedure TIntAsmStorage.SetItems(Index: Integer; Value: TAsm);
begin

  // Set the instruction at the given index
  FAsmCode[Index]:=Pointer(Value.dwInstr);

end;

function TIntAsmStorage.GetItems(Index: Integer): TAsm;
begin

  // Return the instruction at the given index
  result.dwInstr:=Integer(FAsmCode[Index]);

end;

function TIntAsmStorage.GetCount: Integer;
begin

  // Return count of instructions
  result:=FAsmCode.Count

end;

constructor TIntAsmStorage.Create;
begin

  // Perform inherited
  inherited Create;

  // Create the instruction list
  FAsmCode:=TList.Create;

end;

destructor TIntAsmStorage.Destroy;
begin

  // Free the instruction list
  FAsmCode.Free;

  // Perform inherited
  inherited Destroy;

end;

procedure TIntAsmStorage.Clear;
begin

  // Clear the instructions
  FAsmCode.Clear;

end;

function TIntAsmStorage.Add(OpCode: Word = OP_NOP; Index: Word = 0): Integer;
var  OpAsm:         TAsm;
begin

  // Build new opcode instruction
  OpAsm.wOpCode:=OpCode;
  OpAsm.wIndex:=Index;

  // Add the integer value to the list
  result:=FAsmCode.Add(Pointer(OpAsm.dwInstr));

end;

////////////////////////////////////////////////////////////////////////////////
//   TIntStack
////////////////////////////////////////////////////////////////////////////////
function TIntStack.GetCount: Integer;
begin

  // Return count of items on the stack
  result:=FStack.Count;

end;

constructor TIntStack.Create;
begin

  // Perform inherited
  inherited Create;

  // Create stack list
  FStack:=TList.Create;

end;

destructor TIntStack.Destroy;
begin

  // Free the stack list
  FStack.Free;

  // Perform inherited
  inherited Destroy;

end;

procedure TIntStack.Clear;
begin

  // Clear the stack
  FStack.Clear;

end;

procedure TIntStack.Push(Value: Integer);
begin

  // Push the value onto the stack
  FStack.Add(Pointer(Value));

end;

procedure TIntStack.Push(Value: Boolean);
begin

  // Convert boolean and push the value onto the stack
  FStack.Add(Pointer(Ord(Value)));

end;

function TIntStack.Pop: Integer;
var  dwIndex:    Integer;
begin

  // Get the top item
  dwIndex:=Pred(FStack.Count);

  // Set return value
  result:=Integer(FStack[dwIndex]);

  // Remove from the list
  FStack.Delete(dwIndex);

end;

////////////////////////////////////////////////////////////////////////////////
//   TIntStorage
////////////////////////////////////////////////////////////////////////////////
function TIntStorage.GetVarName(Index: Integer): String;
begin

  // Get the name of the variable
  result:=FStorage[Index];

end;

function TIntStorage.GetVarCount: Integer;
begin

  // Return count of variables
  result:=FStorage.Count;

end;

constructor TIntStorage.Create;
begin

  // Perform inherited
  inherited Create;

  // Create storage list
  FStorage:=TStringList.Create;

end;

destructor TIntStorage.Destroy;
begin

  // Free the storage
  FStorage.Free;

  // Perform inherited
  inherited Destroy;

end;

procedure TIntStorage.Clear;
begin

  // Clear the storage
  FStorage.Clear;

end;

function TIntStorage.Add(VarName: String): Integer;
var  dwIndex:       Integer;
begin

  // Check list for variable
  dwIndex:=FStorage.IndexOf(VarName);

  // Is the var name in the list?
  if (dwIndex < 0) then
     // Add it now
     result:=FStorage.Add(VarName)
  else
     // Return the index
     result:=dwIndex;

end;

function TIntStorage.IndexOf(VarName: String): Integer;
begin

  // Return the index of the var name
  result:=FStorage.IndexOf(VarName);

end;

initialization

  // Create identifier list
  IdentList:=TStringList.Create;

  // Add identifiers to the list
  IdentList.AddObject('show', Pointer(ptShow));
  IdentList.AddObject('div', Pointer(ptDiv));
  IdentList.AddObject('mod', Pointer(ptMod));
  IdentList.AddObject('or', Pointer(ptOr));
  IdentList.AddObject('and', Pointer(ptAnd));
  IdentList.AddObject('xor', Pointer(ptXor));
  IdentList.AddObject('not', Pointer(ptNot));
  IdentList.AddObject('shl', Pointer(ptShl));
  IdentList.AddObject('shr', Pointer(ptShr));
  IdentList.AddObject('abs', Pointer(ptAbs));
  IdentList.AddObject('min', Pointer(ptMin));
  IdentList.AddObject('max', Pointer(ptMax));
  IdentList.AddObject('if', Pointer(ptIf));
  IdentList.AddObject('then', Pointer(ptThen));
  IdentList.AddObject('else', Pointer(ptElse));
  IdentList.AddObject('endif', Pointer(ptEndif));

  // Keep the list sorted
  IdentList.Sorted:=True;

finalization

  // Free the identifier list
  IdentList.Free;

end.

0
 
LVL 4

Expert Comment

by:ceoworks
ID: 10954544
What an answer !!! wow
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10955905

Thank you Oktay. It is my hope that this will provide useful for others as well...

Regards,
Russell

0
 
LVL 3

Expert Comment

by:MikProg
ID: 10956275
Is this code is based on knowlege take from any monography or book or other source? if so I need to read it. Can you direct me, please?
0
 
LVL 3

Author Comment

by:Kunfufaresi
ID: 10958822
Hello,

Thanks, i'm working on my code, your steps are very usefull. The idea of interpreter and compiler is making alot of difference like converting
a := 44;

to

=
12
44

i have an array of variables. "a" in this case is the 12th, which the interpreter has assigned it to, so the compiler can do the whole operation without any string operations at all the speed gain is very pleasing. Also dont have to worry about error checking in compiler as they already are solved with the interpreter. So thanks again.

Kunfu Faresi
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 10978400
MikProg,

Some of the techniques used are those that are taught (via books, online info, etc) while others are those that have come from experience spent writing numerous interpreters, and refining the process along the way.

A couple of sites that were helpful to me:

Example implementations:
http://homepages.cwi.nl/~steven/pascal/
http://www.moorecad.com/standardpascal/basics.pas
http://root.cern.ch/root/Cint.html
http://www.programmersheaven.com/zone22/cat207/

Books:
http://www.wkonline.com/a/Writing_Compilers_and_Interpreters_0471113530.htm

---

But alot of it comes from just working on it, understanding the common techniques, and then adding enhancements as you go along

Hope this helps,
Russell


0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Objective: - This article will help user in how to convert their numeric value become words. How to use 1. You can copy this code in your Unit as function 2. than you can perform your function by type this code The Code   (CODE) The Im…
Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
This video discusses moving either the default database or any database to a new volume.
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now