Added support for On-Goto statement:
authorMichael Welch <michaelgwelch@gmail.com>
Sat, 3 Feb 2007 21:20:56 +0000 (21:20 +0000)
committerMichael Welch <michaelgwelch@gmail.com>
Sat, 3 Feb 2007 21:20:56 +0000 (21:20 +0000)
mbasic.csproj:
Treat warnings as errors.
Added CompilerException and TypeCheckException classes.
Added OnGoto class.

Parser.cs:
Added support for parsing OnGoto.

Program.cs:
For statements that do jumps to be able to validate the destination
line numbers exist, I needed to RecordLabels before doing type checking.

Goto.cs:
Check destination label

Gosub.cs:
Check destination label

OnGoto.cs:
Finish implementation.

mbasic/Parser.cs
mbasic/Program.cs
mbasic/SyntaxTree/BuiltInsMethodCall.cs
mbasic/SyntaxTree/CompilerException.cs [new file with mode: 0644]
mbasic/SyntaxTree/Gosub.cs
mbasic/SyntaxTree/Goto.cs
mbasic/SyntaxTree/OnGoto.cs
mbasic/SyntaxTree/TypeCheckException.cs [new file with mode: 0644]
mbasic/mbasic.csproj

index bd2729d..95e4206 100644 (file)
@@ -121,12 +121,34 @@ namespace mbasic
                 case Token.Option:
                     retVal = OptionBaseStatement();
                     break;
+                case Token.On:
+                    retVal = OnGotoStatement();
+                    break;
 
             }
             Match(Token.EndOfLine);
             return retVal;
         }
 
+        private Statement OnGotoStatement()
+        {
+            LineId line = lexer.LineId;
+            Match(Token.On);
+            Expression expr = Expression();
+            Match(Token.Goto);
+
+            List<string> labelNumbers = new List<string>();
+            labelNumbers.Add(lexer.Value);
+            Match(Token.Number);
+            while (lookahead == Token.Comma)
+            {
+                Match(Token.Comma);
+                labelNumbers.Add(lexer.Value);
+                Match(Token.Number);
+            }
+            return new OnGoto(expr, labelNumbers, line);
+        }
+
         private Statement OptionBaseStatement()
         {
             Match(Token.Option);
index d835c7e..a4400c1 100644 (file)
@@ -88,6 +88,7 @@ namespace mbasic
             Node.writer = mbldr.DefineDocument(fileName, Guid.Empty, Guid.Empty, Guid.Empty);
             Node.debug = debug;
             Node.labels = new LabelList();
+            n.RecordLabels(gen);
 
             n.CheckTypes();
             // Create local variables
@@ -104,7 +105,6 @@ namespace mbasic
             Node.locals = locals;
 
 
-            n.RecordLabels(gen);
 
             #region Initialize locals
             // Emit a call to BuiltIns.OptionBase to set
@@ -219,7 +219,7 @@ namespace mbasic
             symbols.ReserveWord("INPUT", Token.Input);
             symbols.ReserveWord("LET", Token.Let);
             symbols.ReserveWord("NEXT", Token.Next);
-            symbols.ReserveWrod("ON", Token.On);
+            symbols.ReserveWord("ON", Token.On);
             symbols.ReserveWord("OPTION", Token.Option);
             symbols.ReserveWord("PRINT", Token.Print);
             symbols.ReserveWord("READ", Token.Read);
index b84f757..da6af0a 100644 (file)
@@ -21,6 +21,8 @@ namespace mbasic.SyntaxTree
             builtInsType.GetMethod("CreateStringArray");
         private static readonly MethodInfo createNumberArray =
             builtInsType.GetMethod("CreateNumberArray");
+        private static readonly MethodInfo badValueError =
+            builtInsType.GetMethod("BadValueError");
 
         private MethodInfo method;
         private BasicType type;
@@ -72,5 +74,9 @@ namespace mbasic.SyntaxTree
             return new BuiltInsMethodCall(createNumberArray);
         }
 
+        public static BuiltInsMethodCall BadValueError()
+        {
+            return new BuiltInsMethodCall(badValueError);
+        }
     }
 }
diff --git a/mbasic/SyntaxTree/CompilerException.cs b/mbasic/SyntaxTree/CompilerException.cs
new file mode 100644 (file)
index 0000000..077b7f6
--- /dev/null
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace mbasic.SyntaxTree
+{
+    class CompilerException : ApplicationException
+    {
+        LineId line;
+        public CompilerException()
+        {
+        }
+
+        public CompilerException(string message, LineId line)
+            : base(message)
+        {
+            this.line = line;
+        }
+
+        public CompilerException(string message, LineId line, Exception innerException)
+            : base(message, innerException)
+        {
+            this.line = line;
+        }
+
+        public LineId LineId { get { return line; } }
+
+
+    }
+}
index 094df49..c5f569b 100644 (file)
@@ -43,7 +43,12 @@ namespace mbasic.SyntaxTree
 
         public override void CheckTypes()
         {
-            // TODO: Check destination label
+            if (!labels.ContainsKey(destLabel))
+            {
+                throw new TypeCheckException(
+                    String.Format("Non existent line number {0} in Gosub statement", destLabel),
+                    line);
+            }
         }
 
         public override void Emit(ILGenerator gen, bool labelSetAlready)
index 5ffc772..2ab2c86 100644 (file)
@@ -37,11 +37,12 @@ namespace mbasic.SyntaxTree
 
         public override void CheckTypes()
         {
-        }
-
-        public override void Emit(ILGenerator gen)
-        {
-            Emit(gen, false);
+            if (!labels.ContainsKey(destLabel))
+            {
+                throw new TypeCheckException(
+                    String.Format("Non existent line number {0} in Goto statement", destLabel),
+                    line);
+            }
         }
 
         public override void Emit(ILGenerator gen, bool labelSetAlready)
index 7a25fa2..34c2c2f 100644 (file)
@@ -23,22 +23,70 @@ namespace mbasic.SyntaxTree
     using System;
     using System.Collections.Generic;
     using System.Reflection.Emit;
+    using TiBasicRuntime;
 
     class OnGoto : Statement
     {
         private List<string> targets;
+        private Expression numericExpression;
+        private BasicType expressionType;
 
         public OnGoto(Expression number, List<string> targets, LineId line)
             : base(line)
         {
+            this.numericExpression = number;
+            this.targets = targets;
         }
 
-        public void CheckType()
+        public override void CheckTypes()
         {
+            // Need to make sure that numericExpression is either
+            // a number or a boolean
+            expressionType = numericExpression.GetBasicType();
+
+            // In this case we can't even allow Booleans which can only have
+            // value of -1 or 0, neither of which is legal in an On-Goto
+            if (expressionType != BasicType.Number)
+            {
+                throw new TypeCheckException("The expression used in a On-Goto statement must be numeric", line);
+            }
+
+            foreach (string target in targets)
+            {
+                if (!labels.ContainsKey(target))
+                {
+                    throw new TypeCheckException(
+                        String.Format("Non existent line number {0} in On-Goto statement", target),
+                        line);
+                }
+            }
         }
 
-        public void Emit(ILGenerator gen)
+        public override void Emit(ILGenerator gen, bool labelSetAlready)
         {
+            if (!labelSetAlready) MarkLabel(gen);
+            MarkSequencePoint(gen);
+            numericExpression.Emit(gen);
+            gen.Emit(OpCodes.Call, typeof(BuiltIns).GetMethod("Round"));
+
+            // We need to subtract one from the expression. OnGoto starts
+            // counting at 1 and switch starts counting at 0.
+            gen.Emit(OpCodes.Ldc_I4_1);
+            gen.Emit(OpCodes.Sub);
+
+            List<Label> destinations = new List<Label>();
+            foreach (string target in targets)
+            {
+               destinations.Add(labels[target]);
+            }
+            gen.Emit(OpCodes.Switch, destinations.ToArray());
+            
+            // if the integer was larger than number of labels, then
+            // switch falls thru to next statement. An On-Goto should
+            // output BAD VALUE IN xx  error.
+            gen.Emit(OpCodes.Ldstr, line.Label);
+            BuiltInsMethodCall.BadValueError().Emit(gen);
+
         }
     }
 }
diff --git a/mbasic/SyntaxTree/TypeCheckException.cs b/mbasic/SyntaxTree/TypeCheckException.cs
new file mode 100644 (file)
index 0000000..885ed6f
--- /dev/null
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace mbasic.SyntaxTree
+{
+    class TypeCheckException : CompilerException
+    {
+        public TypeCheckException()
+        {
+        }
+
+        public TypeCheckException(string message, LineId line)
+            : base(message, line)
+        {
+        }
+
+        public TypeCheckException(string message, LineId line, Exception innerException)
+            : base(message, line, innerException)
+        {
+        }
+
+    }
+}
index d5c399e..53a3d7d 100644 (file)
@@ -18,6 +18,7 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>\r
     <ErrorReport>prompt</ErrorReport>\r
     <WarningLevel>4</WarningLevel>\r
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\r
   </PropertyGroup>\r
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">\r
     <DebugType>pdbonly</DebugType>\r
@@ -26,6 +27,7 @@
     <DefineConstants>TRACE</DefineConstants>\r
     <ErrorReport>prompt</ErrorReport>\r
     <WarningLevel>4</WarningLevel>\r
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>\r
   </PropertyGroup>\r
   <ItemGroup>\r
     <Reference Include="System" />\r
@@ -48,6 +50,7 @@
     <Compile Include="SyntaxTree\BinaryOperator.cs" />\r
     <Compile Include="SyntaxTree\Block.cs" />\r
     <Compile Include="SyntaxTree\BuiltInsMethodCall.cs" />\r
+    <Compile Include="SyntaxTree\CompilerException.cs" />\r
     <Compile Include="SyntaxTree\Concatenate.cs" />\r
     <Compile Include="SyntaxTree\Data.cs" />\r
     <Compile Include="SyntaxTree\Division.cs" />\r
@@ -66,6 +69,7 @@
     <Compile Include="SyntaxTree\NumberLiteral.cs" />\r
     <Compile Include="SyntaxTree\GreaterThan.cs" />\r
     <Compile Include="SyntaxTree\Node.cs" />\r
+    <Compile Include="SyntaxTree\OnGoto.cs" />\r
     <Compile Include="SyntaxTree\OptionBaseStatement.cs" />\r
     <Compile Include="SyntaxTree\Power.cs" />\r
     <Compile Include="SyntaxTree\Randomize.cs" />\r
@@ -78,6 +82,7 @@
     <Compile Include="SyntaxTree\Subroutine.cs" />\r
     <Compile Include="SyntaxTree\Subtract.cs" />\r
     <Compile Include="SyntaxTree\Tab.cs" />\r
+    <Compile Include="SyntaxTree\TypeCheckException.cs" />\r
     <Compile Include="SyntaxTree\VariableReference.cs" />\r
     <Compile Include="SyntaxTree\Print.cs" />\r
     <Compile Include="SyntaxTree\StringLiteral.cs" />\r