Added support for multiple variable variable list in input statement.
authorMichael Welch <michaelgwelch@gmail.com>
Wed, 10 Jan 2007 02:35:40 +0000 (02:35 +0000)
committerMichael Welch <michaelgwelch@gmail.com>
Wed, 10 Jan 2007 02:35:40 +0000 (02:35 +0000)
13 files changed:
TiBasicRuntime/BuiltIns.cs
TiBasicRuntime/InputParser.cs [new file with mode: 0644]
TiBasicRuntime/Radix100.cs
TiBasicRuntime/TestRadix100.cs
TiBasicRuntime/TiBasicRuntime.csproj
mbasic/Parser.cs
mbasic/SyntaxTree/Assign.cs
mbasic/SyntaxTree/BuiltInsMethodCall.cs
mbasic/SyntaxTree/Input.cs
mbasic/SyntaxTree/Statement.cs
mbasic/SyntaxTree/VariableReference.cs
mbasic/mbasic.csproj
samples/secretnum.mbas

index f6bfef3..83f16fa 100644 (file)
@@ -371,5 +371,58 @@ namespace TiBasicRuntime
             return gosubs.Pop();
         }
         #endregion
+
+        private static string[] consoleValues;
+        private static int consoleValuesIndex;
+        public static void ReadLineFromConsoleIntoBuffer(int expectedNumberOfValues)
+        {
+            string line = Console.ReadLine();
+            printCol = 1; 
+            InputParser parser = new InputParser(line);
+            List<string> strings = new List<string>();
+            while (!parser.EndOfString) strings.Add(parser.Next());
+
+            if (strings.Count == 0) // then the user just hit enter, or spaces and enter, which is considered a valid zero length string
+            {
+                strings.Add(String.Empty); // This entry should be considered a zero length string.
+            }
+
+
+            while (strings.Count != expectedNumberOfValues)
+            {
+                Console.WriteLine("* WARNING:");
+                Console.WriteLine("  INPUT ERROR");
+                Print("TRY AGAIN: ","\0");
+                line = Console.ReadLine();
+                parser = new InputParser(line);
+                strings = new List<string>();
+                while (!parser.EndOfString) strings.Add(parser.Next());
+            }
+            consoleValues = strings.ToArray();
+            consoleValuesIndex = 0;
+
+        }
+
+        public static double ReadNumberFromConsole()
+        {
+            string s = consoleValues[consoleValuesIndex];
+            double d;
+            if (!double.TryParse(s, out d))
+            {
+                Console.WriteLine("* WARNING:");
+                Console.WriteLine("  INPUT ERROR");
+                Print("TRY AGAIN: ","\0");
+                throw new InvalidCastException();
+            }
+            consoleValuesIndex++;
+            return d;
+        }
+
+        public static string ReadStringFromConsole()
+        {
+            string s = consoleValues[consoleValuesIndex];
+            consoleValuesIndex++;
+            return s;
+        }
     }
 }
diff --git a/TiBasicRuntime/InputParser.cs b/TiBasicRuntime/InputParser.cs
new file mode 100644 (file)
index 0000000..305ed23
--- /dev/null
@@ -0,0 +1,125 @@
+using System;\r
+using System.Collections.Generic;\r
+using System.Text;\r
+using System.IO;\r
+\r
+namespace TiBasicRuntime\r
+{\r
+    /// <summary>\r
+    /// Reads a line of input and breaks it into comma seperated values.\r
+    /// </summary>\r
+    class InputParser\r
+    {\r
+        Reader reader;\r
+        public InputParser(string s)\r
+        {\r
+            this.reader = new Reader(s.Trim() + " ");\r
+        }\r
+\r
+        public bool EndOfString { get { return reader.EndOfStream; } }\r
+\r
+        public string Next()\r
+        {\r
+            while (true)\r
+            {\r
+                char ch = reader.Current;\r
+                if (char.IsWhiteSpace(ch))\r
+                {\r
+                    reader.Advance();\r
+                    continue;\r
+                }\r
+\r
+                string val = NextString(ch == '\"');\r
+\r
+                while (!reader.EndOfStream)\r
+                {\r
+                    ch = reader.Current;\r
+                    if (char.IsWhiteSpace(ch) || ch == ',') reader.Advance();\r
+                    else break;\r
+                }\r
+                return val;\r
+            }\r
+        }\r
+\r
+        private string NextString(bool quoted)\r
+        {\r
+            Char endChar = quoted ? '\"' : ',';\r
+            StringBuilder bldr = new StringBuilder();\r
+            if (!quoted) bldr.Append(reader.Current); // If not quoted then append current char, otherwise skip it\r
+\r
+            do\r
+            {\r
+                for (char ch = reader.Read(); ch != endChar && !reader.EndOfStream; ch = reader.Read())\r
+                {\r
+                    bldr.Append(ch);\r
+                }\r
+\r
+                if (quoted) reader.Advance(); // to consume the quotation mark\r
+\r
+                // if the next character is a quote then we haven't reached the end of\r
+                // the string. We just read a double quote "" in the string which\r
+                // should be replaced with a "\r
+\r
+                if (reader.Current == '\"') bldr.Append('\"');\r
+\r
+            } while (quoted && reader.Current == '\"');\r
+\r
+            string value = bldr.ToString();\r
+            return value;\r
+\r
+        }\r
+\r
+        private string NextString()\r
+        {\r
+            return NextString(true);\r
+        }\r
+\r
+        private class Reader\r
+        {\r
+            int index = 0;\r
+            string s;\r
+            int length;\r
+\r
+            public Reader(String s)\r
+            {\r
+                this.s = s;\r
+                this.length = s.Length;\r
+            }\r
+\r
+            /// <summary>\r
+            /// Gets the character at the current position but does not change the current position.\r
+            /// </summary>\r
+            /// <returns></returns>\r
+            public char Current\r
+            {\r
+                get\r
+                {\r
+                    return s[index];\r
+                }\r
+            }\r
+\r
+            /// <summary>\r
+            /// Moves the reader to the next character.\r
+            /// </summary>\r
+            public void Advance()\r
+            {\r
+                index++;\r
+            }\r
+\r
+            /// <summary>\r
+            /// Moves the reader to the next character and returns it.\r
+            /// Identical to Advance() followed by reading Current;\r
+            /// </summary>\r
+            /// <returns></returns>\r
+            public char Read()\r
+            {\r
+                Advance();\r
+                return Current;\r
+            }\r
+\r
+            public bool EndOfStream { get { return index == length - 1; } }\r
+\r
+        }\r
+\r
+    }\r
+}\r
index b6821d3..c7fac9b 100644 (file)
@@ -36,9 +36,9 @@ namespace TiBasicRuntime
         // The specs say that the Exp and MSD should be complemented but I don't see the need
         // so i won't for my implementation.
 
-        public static readonly Radix100 MaxValue = new Radix100(0x7FFFFFFFFFFFFFFF);
-        public static readonly Radix100 MinValue = new Radix100(0x8001FFFFFFFFFFFF);
-        public static readonly Radix100 Epsilon = new Radix100(0x0001000000000000);
+        public static readonly Radix100 MaxValue = new Radix100(0x7F63636363636363); // 9.9999999999999E127
+        public static readonly Radix100 MinValue = new Radix100(0xFF63636363636363); //-9.9999999999999E127
+        public static readonly Radix100 Epsilon = new Radix100(0x0001000000000000);  // 1E-128
         public static readonly Radix100 Zero = new Radix100(0x4000000000000000);
         public static readonly Radix100 One = new Radix100(0x4001000000000000);
 
@@ -212,6 +212,7 @@ namespace TiBasicRuntime
 
 
 
+
         #region Helper functions, masks, constants,
 
         public static byte BiasedExponentValue(sbyte normalizedExponent)
index da9ed10..dd232e6 100644 (file)
@@ -15,6 +15,14 @@ namespace TiBasicRuntime
             Assert.IsFalse(Radix100.MinValue.IsInteger, "min");
             Assert.IsFalse(Radix100.Epsilon.IsInteger, "epsilon");
             Assert.IsTrue(Radix100.Zero.IsInteger, "zero");
+
+            string maxValue = "9.99999E+**";
+            string minValue = "-9.99999E+**";
+            double epsilon = 1E-128;
+            Assert.AreEqual(maxValue, Radix100.MaxValue.ToString());
+            Assert.AreEqual(minValue, Radix100.MinValue.ToString());
+            Assert.AreEqual(epsilon, (double)Radix100.Epsilon, (double)Radix100.Epsilon);
+
         }
 
         [Test]
index 861ce65..2af33a8 100644 (file)
@@ -7,7 +7,7 @@
     <ProjectGuid>{3EF3FB61-5A24-4268-90EC-8FF9101CEF7D}</ProjectGuid>\r
     <OutputType>Library</OutputType>\r
     <AppDesignerFolder>Properties</AppDesignerFolder>\r
-    <RootNamespace>TiBasicRuntiime.dll</RootNamespace>\r
+    <RootNamespace>TiBasicRuntime</RootNamespace>\r
     <AssemblyName>TiBasicRuntime</AssemblyName>\r
   </PropertyGroup>\r
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">\r
@@ -37,6 +37,7 @@
   </ItemGroup>\r
   <ItemGroup>\r
     <Compile Include="BuiltIns.cs" />\r
+    <Compile Include="InputParser.cs" />\r
     <Compile Include="Properties\AssemblyInfo.cs" />\r
     <Compile Include="Radix100.cs" />\r
     <Compile Include="TestRadix100.cs" />\r
index 5a88c7b..f72ff26 100644 (file)
@@ -265,11 +265,62 @@ namespace mbasic
 
         private Statement InputStatement()
         {
+            // Requires more than one token of lookahead. If we encounter a string variable
+            // after input, it could be an input prompt or the variable to store the input in.
+            // It's not until we look past that and check for a colon do we know.
+
             LineId line = lexer.LineId;
             Match(Token.Input);
-            int index = lexer.SymbolIndex;
-            Match(Token.Variable);
-            return new Input(index, line);
+            List<Assign> inputs = new List<Assign>();
+            Expression inputPrompt;
+
+            // Assume that an input prompt is given and parse it as an expression
+            // If it later turns out that it is not the input prompt, then it must
+            // be convert to an assignment.
+
+            Expression expr = Expression();
+
+            if (lookahead == Token.Colon)
+            {
+                inputPrompt = expr;
+                Match(Token.Colon);
+            }
+            else // if there is no colon then what we read shouldn't be
+                // treated as an Expression, but as an Assignment.
+            {
+                inputPrompt = new StringLiteral("? ", LineId.None);
+                VariableReference vr = (VariableReference)expr; // The expr must have been a Variable Reference
+                int symbolIndex = vr.SymbolIndex;
+                switch (symbols[symbolIndex].BasicType)
+                {
+                    case BasicType.Number:
+                        inputs.Add(Assign.ReadNumberFromConsole(symbolIndex, line));
+                        break;
+                    case BasicType.String:
+                        inputs.Add(Assign.ReadStringFromConsole(symbolIndex, line));
+                        break;
+                }
+                if (lookahead == Token.Comma) Match(Token.Comma);
+            }
+
+
+            while (lookahead != Token.EOF && lookahead != Token.EndOfLine)
+            {
+                int symbolIndex = lexer.SymbolIndex;
+                switch (symbols[symbolIndex].BasicType)
+                {
+                    case BasicType.Number:
+                        inputs.Add(Assign.ReadNumberFromConsole(symbolIndex, line));
+                        break;
+                    case BasicType.String:
+                        inputs.Add(Assign.ReadStringFromConsole(symbolIndex, line));
+                        break;
+                }
+                Match(Token.Variable);
+                if (lookahead == Token.Comma) Match(Token.Comma);
+            }
+            return new Input(inputPrompt, inputs.ToArray(), line);
+
         }
 
         private Statement AssignStatement()
index 1e0a59d..565d436 100644 (file)
@@ -83,12 +83,12 @@ namespace mbasic.SyntaxTree
 
         public static Assign ReadStringFromConsole(int symbolIndex, LineId line)
         {
-            throw new NotImplementedException();
+            return new Assign(symbolIndex, BuiltInsMethodCall.ReadStringFromConsole(), line);
         }
 
         public static Assign ReadNumberFromConsole(int symbolIndex, LineId line)
         {
-            throw new NotImplementedException();
+            return new Assign(symbolIndex, BuiltInsMethodCall.ReadNumberFromConsole(), line);
         }
 
     }
index 1b4382d..bb17c31 100644 (file)
@@ -13,6 +13,10 @@ namespace mbasic.SyntaxTree
             builtInsType.GetMethod("ReadStringFromData");\r
         private static readonly MethodInfo readNumberFromData =\r
             builtInsType.GetMethod("ReadNumberFromData");\r
+        private static readonly MethodInfo readStringFromConsole =\r
+            builtInsType.GetMethod("ReadStringFromConsole");\r
+        private static readonly MethodInfo readNumberFromConsole =\r
+            builtInsType.GetMethod("ReadNumberFromConsole");\r
 \r
         private MethodInfo method;\r
         private BasicType type;\r
@@ -44,5 +48,15 @@ namespace mbasic.SyntaxTree
             return new BuiltInsMethodCall(readNumberFromData);\r
         }\r
 \r
+        public static BuiltInsMethodCall ReadStringFromConsole()\r
+        {\r
+            return new BuiltInsMethodCall(readStringFromConsole);\r
+        }\r
+\r
+        public static BuiltInsMethodCall ReadNumberFromConsole()\r
+        {\r
+            return new BuiltInsMethodCall(readNumberFromConsole);\r
+        }\r
+\r
     }\r
 }\r
index 06b90ce..1b407ec 100644 (file)
@@ -24,59 +24,54 @@ using System.Collections.Generic;
 using System.Text;
 using System.Reflection;
 using System.Reflection.Emit;
+using TiBasicRuntime;
+
 namespace mbasic.SyntaxTree
 {
     class Input : Statement
     {
+
         private static readonly MethodInfo readLineMethod =
-            typeof(Console).GetMethod("ReadLine");
-
-        private static readonly MethodInfo tryParseMethod =
-            typeof(double).GetMethod("TryParse",
-            new Type[] { typeof(string), Type.GetType("System.Double&") });
-        int index;
-        BasicType varType;
-        public Input(int index, LineId line)
-            : base(line)
-        {
-            this.index = index; 
-        }
+            typeof(BuiltIns).GetMethod("ReadLineFromConsoleIntoBuffer");
 
-        public override void Emit(ILGenerator gen)
+        Assign[] inputs;
+        Print inputPrompt;
+        public Input(Expression inputPromptExpr, Assign[] inputs, LineId line)
+            : base(line)
         {
-            Emit(gen, false);
+            this.inputPrompt = new Print(
+                new Expression[] { inputPromptExpr, new StringLiteral("\0", line) }, line);
+            this.inputs = inputs;
         }
 
         public override void Emit(ILGenerator gen, bool labelSetAlready)
         {
             if (!labelSetAlready) MarkLabel(gen);
-            Label readLine = gen.DefineLabel();
             MarkSequencePoint(gen);
-            gen.MarkLabel(readLine);
-            gen.Emit(OpCodes.Call, readLineMethod);
-            if (varType == BasicType.Number)
+
+            inputPrompt.Emit(gen, true);
+
+            Label begin = gen.DefineLabel();
+            gen.MarkLabel(begin);
+            gen.BeginExceptionBlock();
+
+            // Expected number of inputs:
+            gen.Emit(OpCodes.Ldc_I4, inputs.Length);
+            gen.Emit(OpCodes.Call, readLineMethod); // reads a line from console into a buffer and checks to make sure number of values equals expected number of values.
+
+            foreach (Assign input in inputs)
             {
-                // Try to parse it into a double, if not possible give a warning and read again.
-                gen.Emit(OpCodes.Ldloca, (short) index);
-                gen.Emit(OpCodes.Call, tryParseMethod);
-                // if return value is false, the parse failed. go back to read line
-                gen.Emit(OpCodes.Brfalse_S, readLine);
-
-                // Else it was successful, the local variable should have the value.
-                return;
+                input.Emit(gen, true);
             }
-            else gen.Emit(OpCodes.Stloc, locals[index]);
 
+            gen.BeginCatchBlock(typeof(InvalidCastException));
+            gen.Emit(OpCodes.Leave, begin);
+            gen.EndExceptionBlock();
         }
 
         public override void CheckTypes()
         {
-            Type t = locals[index].LocalType;
-
-            if (t == typeof(string))varType = BasicType.String;
-            else varType = BasicType.Number;
-
-
+            inputPrompt.CheckTypes();
         }
 
     }
index 74e7e0b..7779f60 100644 (file)
@@ -46,7 +46,7 @@ namespace mbasic.SyntaxTree
         private static List<int> lines = new List<int>();
         protected void MarkSequencePoint(ILGenerator gen)
         {
-            if (debug & lines.BinarySearch(line.Number) < 0)
+            if (debug & lines.BinarySearch(line.Number) < 0 && !line.Equals(LineId.None))
             {
                 gen.MarkSequencePoint(writer, line.Number, 1, line.Number, 100);
                 lines.Add(line.Number);
index 84d1761..e992a94 100644 (file)
@@ -35,6 +35,8 @@ namespace mbasic.SyntaxTree
             this.index = index;
         }
 
+        public int SymbolIndex { get { return index; } }
+
         public override void Emit(ILGenerator gen)
         {
             if (index < 255)
index f118e7b..ae541eb 100644 (file)
@@ -97,6 +97,9 @@
     <None Include="..\samples\helloworld.mbas">\r
       <Link>samples\helloworld.mbas</Link>\r
     </None>\r
+    <None Include="..\samples\input.mbas">\r
+      <Link>samples\input.mbas</Link>\r
+    </None>\r
     <None Include="..\samples\print.mbas">\r
       <Link>samples\print.mbas</Link>\r
     </None>\r
index e9b706f..e4ed18c 100644 (file)
@@ -4,16 +4,12 @@
 
 140 CALL CLEAR
 
-150 PRINT "ENTER LIMIT"
-151 INPUT LIMIT
+150 INPUT "ENTER LIMIT? ":LIMIT
 160 SECRET = INT(LIMIT*RND)+1
 170 CALL CLEAR
 180 N=N+1
 
-190 PRINT "LOW GUESS:"
-191 INPUT LOW
-192 PRINT "HIGH GUESS:"
-193 INPUT HIGH
+190 INPUT "LOW,HIGH GUESSES: ":LOW, HIGH
 200 IF LOW<>HIGH THEN 220
 210 IF SECRET=LOW THEN 300
 220 IF SECRET<LOW THEN 260
 310 PRINT "NUMBER IN";N;"TRIES"
 
 320 PRINT "WANT TO PLAY AGAIN?"
-330 PRINT "ENTER Y OR N: "
-331 INPUT A$
+330 INPUT "ENTER Y OR N: ":A$
 340 IF A$<>"Y" THEN 390
 350 N=0
 360 PRINT "WANT TO SET A NEW LIMIT?"
-371 PRINT "ENTER Y OR N: "
-372 INPUT B$
+371 INPUT "ENTER Y OR N: ":B$
 380 IF B$="Y" THEN 140 
 381 GOTO 160
 390 PRINT "DONE"