From 04f56c12e82ea5228b2b65e68c46ed7f4563182b Mon Sep 17 00:00:00 2001
From: Mike Buland <mike@xagasoft.com>
Date: Tue, 23 Apr 2013 11:47:01 -0600
Subject: Variables work.

---
 src/lexer.cpp  |  40 ++++++++++----------
 src/parser.cpp | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/parser.h   |   6 +++
 src/token.cpp  |   5 ++-
 src/token.h    |   3 +-
 5 files changed, 138 insertions(+), 30 deletions(-)

(limited to 'src')

diff --git a/src/lexer.cpp b/src/lexer.cpp
index 3ebb0cf..46686c8 100644
--- a/src/lexer.cpp
+++ b/src/lexer.cpp
@@ -63,6 +63,26 @@ Token Lexer::nextToken()
                 }
                 break;
 
+            case '$':
+                {
+                    Bu::String *sTmp = new Bu::String();
+                    for( iBufPos++; iBufPos < sBuf.getSize() &&
+                         sBuf[iBufPos] != ' ' && sBuf[iBufPos] != '\t' &&
+                         sBuf[iBufPos] != '+' && sBuf[iBufPos] != '-' &&
+                         sBuf[iBufPos] != '*' && sBuf[iBufPos] != '/' &&
+                         sBuf[iBufPos] != '(' && sBuf[iBufPos] != ')';
+                         iBufPos++ )
+                    {
+                        sTmp->append( sBuf[iBufPos] );
+                    }
+                    return Token( Token::tVariable, sTmp );
+                }
+                break;
+
+            case '=':
+                iBufPos++;
+                return Token( Token::tEquals );
+
             case '+':
                 iBufPos++;
                 return Token( Token::tPlus );
@@ -115,26 +135,6 @@ Token Lexer::nextToken()
                         delete sTmp;
                         return Token( Token::tNumber, n );
                     }
-                    else if( (sBuf[iBufPos]>=(ascRangeTop+1) && sBuf[iBufPos]<='z') ||
-                            (sBuf[iBufPos]>='A' && sBuf[iBufPos]<='Z') ||
-                            sBuf[iBufPos] == '_' )
-                    {
-                        for( ; iBufPos < sBuf.getSize(); iBufPos++ )
-                        {
-                            if( (sBuf[iBufPos]>='a' && sBuf[iBufPos]<='z') ||
-                                (sBuf[iBufPos]>='A' && sBuf[iBufPos]<='Z') ||
-                                (sBuf[iBufPos]>='0' && sBuf[iBufPos]<='9') ||
-                                sBuf[iBufPos] == '_' )
-                            {
-                                sTmp->append( sBuf[iBufPos] );
-                            }
-                            else
-                            {
-                                break;
-                            }
-                        }
-                        return Token( Token::tString, sTmp );
-                    }
                     else
                     {
                         sBuf.clear();
diff --git a/src/parser.cpp b/src/parser.cpp
index 21ccc11..5bb0aa6 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -28,7 +28,7 @@ void Parser::parse()
                 unwind();
                 if( !tsTerminal.isEmpty() )
                 {
-                    Bu::println( rOut, "%1").arg( *tsTerminal.peek().nVal );
+                    Bu::println( rOut, "%1").arg( deref( tsTerminal.peek() ) );
                 }
                 tsTerminal.clear();
                 break;
@@ -58,6 +58,9 @@ void Parser::parse()
                         {
                             Bu::println( rOut, "Scale changed to: %1").
                                 arg( lex.getScale() );
+                            for( VarHash::iterator i = hVars.begin(); i; i++ )
+                                (*i).setScale( lex.getScale() );
+                            nZero = Number( lex.getScale(), lex.getRadix() );
                         }
                     }
                     else
@@ -85,6 +88,8 @@ void Parser::parse()
                             lex.setRadix( i );
                             Bu::println( rOut, "Radix changed to: %1").
                                 arg( lex.getRadix() );
+                            hVars.clear();
+                            nZero = Number( lex.getScale(), lex.getRadix() );
                         }
                     }
                     else
@@ -93,6 +98,47 @@ void Parser::parse()
                                 "the parameter to radix.");
                     }
                 }
+                else if( *t.sVal == "vars" )
+                {
+                    Bu::println( rOut, "Declared variables:");
+                    for( VarHash::iterator i = hVars.begin(); i; i++ )
+                    {
+                        Bu::println( rOut, " - %1 = %2").
+                            arg( i.getKey() ).arg( *i );
+                    }
+                    Bu::println( rOut, "");
+                }
+                else if( *t.sVal == "help" || *t.sVal == "h" || *t.sVal == "?" )
+                {
+                    Bu::println( rOut,
+                        "All commands start with a '\\'.  The following commands are defined:\n"
+                        " \\help, \\h, \\?   - This help\n"
+                        " \\exit, \\quit    - Exit the program\n"
+                        " \\radix          - Display the current radix (base)\n"
+                        " \\radix <num>    - Set the radix to anything between 2 and 36 (inclusive)\n"
+                        " \\scale          - Display the current scale\n"
+                        " \\scale <num>    - Set the current scale\n"
+                        " \\vars           - List all defined variables\n"
+                        "\n"
+                        "All variables must prefixed with a $ but otherwise can be used anywhere a\n"
+                        "number can be used.  To assign a variable use =, i.e.:\n"
+                        "  $amnt = $rate * 144000.09\n"
+                        "\n"
+                        "When using a radix greater than 10 all extended digits are lowercase letters\n"
+                        "starting with 'a'.  Upper case is not currently supported.\n"
+                        "\n"
+                        "All numbers are interpreted in the current radix, this means that you need to\n"
+                        "set the radix using the current radix, even though the current value is aways\n"
+                        "displayed in decimal.  That is, to go to base 8 and back to base 10 you would:\n"
+                        "  \\radix 8\n"
+                        "  Radix changed to: 8\n"
+                        "  \\radix 12\n"
+                        "  Radix changed to: 10\n"
+                        "\n"
+                        "Changing the radix always clears all variables.\n"
+                        );
+
+                }
                 else
                 {
                     Bu::println( rOut, "ERROR: Unknown command '%1'").
@@ -101,6 +147,7 @@ void Parser::parse()
                 break;
 
             case Token::tNumber:
+            case Token::tVariable:
                 tsTerminal.push( t );
                 break;
 
@@ -150,7 +197,9 @@ void Parser::unwind()
                     Token b = tsTerminal.peekPop();
                     Token a = tsTerminal.peekPop();
                     tsTerminal.push(
-                        Token( Token::tNumber, new Number( *a.nVal + *b.nVal ) )
+                        Token( Token::tNumber,
+                            new Number( deref(a) + deref(b) )
+                            )
                         );
                 }
                 break;
@@ -160,7 +209,9 @@ void Parser::unwind()
                     Token b = tsTerminal.peekPop();
                     Token a = tsTerminal.peekPop();
                     tsTerminal.push(
-                        Token( Token::tNumber, new Number( *a.nVal - *b.nVal ) )
+                        Token( Token::tNumber,
+                            new Number( deref(a) - deref(b) )
+                            )
                         );
                 }
                 break;
@@ -170,7 +221,9 @@ void Parser::unwind()
                     Token b = tsTerminal.peekPop();
                     Token a = tsTerminal.peekPop();
                     tsTerminal.push(
-                        Token( Token::tNumber, new Number( *a.nVal * *b.nVal ) )
+                        Token( Token::tNumber,
+                            new Number( deref(a) * deref(b) )
+                            )
                         );
                 }
                 break;
@@ -180,7 +233,9 @@ void Parser::unwind()
                     Token b = tsTerminal.peekPop();
                     Token a = tsTerminal.peekPop();
                     tsTerminal.push(
-                        Token( Token::tNumber, new Number( *a.nVal / *b.nVal ) )
+                        Token( Token::tNumber,
+                            new Number( deref(a) / deref(b) )
+                            )
                         );
                 }
                 break;
@@ -202,8 +257,28 @@ void Parser::unwind()
                 }
                 break;
 
+            case Token::tEquals:
+                {
+                    Token b = tsTerminal.peekPop();
+                    Token a = tsTerminal.peekPop();
+                    if( a.eType != Token::tVariable )
+                    {
+                        Bu::println("The left hand side of a = must be a variable.");
+                        tsTerminal.clear();
+                        tsNonTerminal.clear();
+                        return;
+                    }
+                    hVars.insert( *a.sVal, deref( b ) );
+                    tsTerminal.push(
+                        Token( Token::tNumber,
+                            new Number( deref( b ) )
+                            )
+                        );
+                }
+                break;
+
             case Token::tNumber:
-            case Token::tString:
+            case Token::tVariable:
             case Token::tCommand:
             case Token::tEndOfLine:
             case Token::tEndOfInput:
@@ -221,6 +296,7 @@ int Parser::reqTokens( Token::Type eType )
         case Token::tMinus:
         case Token::tDivide:
         case Token::tMultiply:
+        case Token::tEquals:
             return 2;
 
         case Token::tOpenParen:
@@ -239,8 +315,9 @@ int Parser::getPriority( Token::Type eType )
     switch( eType )
     {
         case Token::tNumber:
-        case Token::tString:
+        case Token::tVariable:
         case Token::tCommand:
+        case Token::tEquals:
             return 0;
 
         case Token::tPlus:
@@ -264,3 +341,26 @@ int Parser::getPriority( Token::Type eType )
     }
 }
 
+Number &Parser::deref( Token &t )
+{
+    if( t.eType == Token::tNumber )
+    {
+        return *t.nVal;
+    }
+    else if( t.eType == Token::tVariable )
+    {
+        try
+        {
+            return hVars.get( *t.sVal );
+        }
+        catch(...)
+        {
+            return nZero;
+        }
+    }
+    else
+    {
+        throw Bu::ExceptionBase("Element was not a number or variable.");
+    }
+}
+
diff --git a/src/parser.h b/src/parser.h
index 5563613..2b1a4af 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -2,7 +2,9 @@
 #define PARSER_H
 
 #include <bu/list.h>
+#include <bu/hash.h>
 #include "token.h"
+#include "number.h"
 
 namespace Bu
 {
@@ -23,13 +25,17 @@ private:
     void unwind();
     int reqTokens( Token::Type eType );
     int getPriority( Token::Type eType );
+    Number &deref( Token &t );
 
 private:
     Lexer &lex;
     Bu::Stream &rOut;
     typedef Bu::List<Token> TokenStack;
+    typedef Bu::Hash<Bu::String, Number> VarHash;
     TokenStack tsTerminal;
     TokenStack tsNonTerminal;
+    Number nZero;
+    VarHash hVars;
 };
 
 #endif
diff --git a/src/token.cpp b/src/token.cpp
index d7fbe88..019e54d 100644
--- a/src/token.cpp
+++ b/src/token.cpp
@@ -38,7 +38,7 @@ Token::~Token()
             delete nVal;
             break;
 
-        case tString:
+        case tVariable:
         case tCommand:
             delete sVal;
             break;
@@ -53,7 +53,7 @@ Bu::Formatter &operator<<( Bu::Formatter &f, Token::Type eType )
     switch( eType )
     {
         case Token::tNumber:        return f << "num";
-        case Token::tString:        return f << "str";
+        case Token::tVariable:      return f << "var";
         case Token::tCommand:       return f << "cmd";
         case Token::tPlus:          return f << "+";
         case Token::tMinus:         return f << "-";
@@ -61,6 +61,7 @@ Bu::Formatter &operator<<( Bu::Formatter &f, Token::Type eType )
         case Token::tMultiply:      return f << "*";
         case Token::tOpenParen:     return f << "(";
         case Token::tCloseParen:    return f << ")";
+        case Token::tEquals:        return f << "=";
         case Token::tEndOfLine:     return f << "eol";
         case Token::tEndOfInput:    return f << "eoi";
 
diff --git a/src/token.h b/src/token.h
index 3b5caff..05a610a 100644
--- a/src/token.h
+++ b/src/token.h
@@ -14,7 +14,7 @@ public:
     enum Type
     {
         tNumber,
-        tString,
+        tVariable,
         tCommand,
         tPlus,
         tMinus,
@@ -22,6 +22,7 @@ public:
         tMultiply,
         tOpenParen,
         tCloseParen,
+        tEquals,
 
         tEndOfLine,
 
-- 
cgit v1.2.3