Commits

Jesse McGrew  committed 0f6c8c9

Added CompileTimeType and CompileTimeValue types

  • Participants
  • Parent commits c673343

Comments (0)

Files changed (10)

File Rellor.Core/Analyzer.cs

 
         public void Load(ICharStream input)
         {
+            Contract.Requires(input != null);
+
             // lexing
             var lexer = new Inform6Lexer(input);
             lexer.LogSink = this.LogSink;
             // result
             this.SyntaxTree = (ITree)cleanResult;
         }
+
+        public CompileTimeValue EvalConstant(ITree expression)
+        {
+            Contract.Requires(expression != null);
+
+            var treeAdaptor = new RellorTreeAdaptor();
+            var nodeStream = new CommonTreeNodeStream(treeAdaptor, expression);
+            var eval = new EvalConstant(nodeStream)
+            {
+                LogSink = this.LogSink,
+                Scope = this.GlobalScope,
+            };
+
+            return eval.constantExpr();
+        }
     }
 }

File Rellor.Core/CompileTimeType.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Rellor.Core
+{
+    public abstract class CompileTimeType
+    {
+        public abstract string Name { get; }
+
+        //---
+
+        public static readonly CompileTimeType Unknown = new UnknownType();
+        public static readonly CompileTimeType Number = new NumberType();
+        public static readonly CompileTimeType String = new StringType();
+        public static readonly CompileTimeType Object = new ObjectType();
+
+        private class UnknownType : CompileTimeType
+        {
+            public override string Name
+            {
+                get { return "unknown type"; }
+            }
+        }
+
+        private class NumberType : CompileTimeType
+        {
+            public override string Name
+            {
+                get { return "number"; }
+            }
+        }
+
+        private class StringType : CompileTimeType
+        {
+            public override string Name
+            {
+                get { return "string"; }
+            }
+        }
+
+        private class ObjectType : CompileTimeType
+        {
+            public override string Name
+            {
+                get { return "object"; }
+            }
+        }
+    }
+}

File Rellor.Core/CompileTimeValue.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics.Contracts;
+
+namespace Rellor.Core
+{
+    public enum BackpatchType
+    {
+        None,
+
+        Symbol,
+        String,
+        Dictionary,
+        Object,
+        Routine,
+        Array,
+        SysConstant,
+    }
+
+    public class CompileTimeValue
+    {
+        private static readonly CompileTimeValue Zero = new CompileTimeValue(0, CompileTimeType.Number);
+        private static readonly CompileTimeValue One = new CompileTimeValue(1, CompileTimeType.Number);
+
+        public static CompileTimeValue FromInt32(int value)
+        {
+            switch (value)
+            {
+                case 0:
+                    return Zero;
+                case 1:
+                    return One;
+                default:
+                    return new CompileTimeValue(value, CompileTimeType.Number);
+            }
+        }
+
+        public static CompileTimeValue FromString(string value)
+        {
+            return new CompileTimeValue<string>(BackpatchType.String, value, CompileTimeType.String);
+        }
+
+        public CompileTimeValue(int numericValue, CompileTimeType type)
+        {
+            Contract.Requires(type != null);
+
+            this.NumericValue = numericValue;
+            this.BackpatchType = BackpatchType.None;
+            this.Type = type;
+        }
+
+        public CompileTimeValue(BackpatchType backpatchType, CompileTimeType type)
+        {
+            Contract.Requires(backpatchType != BackpatchType.None);
+            Contract.Requires(type != null);
+
+            this.BackpatchType = backpatchType;
+            this.Type = type;
+        }
+
+        public int? NumericValue { get; private set; }
+        public CompileTimeType Type { get; private set; }
+        public BackpatchType BackpatchType { get; private set; }
+
+        public bool NeedsBackpatch
+        {
+            get { return this.BackpatchType != BackpatchType.None; }
+        }
+
+        public virtual object GenericValue
+        {
+            get { return NumericValue; }
+        }
+    }
+
+    public class CompileTimeValue<T> : CompileTimeValue
+    {
+        public CompileTimeValue(int numericValue, T customValue, CompileTimeType type)
+            : base(numericValue, type)
+        {
+            Contract.Requires(type != null);
+
+            this.CustomValue = customValue;
+        }
+
+        public CompileTimeValue(BackpatchType backpatchType, T customValue, CompileTimeType type)
+            : base(backpatchType, type)
+        {
+            Contract.Requires(backpatchType != BackpatchType.None);
+            Contract.Requires(type != null);
+
+            this.CustomValue = customValue;
+        }
+
+        public T CustomValue { get; private set; }
+
+        public override object GenericValue
+        {
+            get { return CustomValue; }
+        }
+    }
+}

File Rellor.Core/EvalConstant.g3

 
 @header{
 using System;
+using Rellor.Core.Token;
 using Rellor.Core.Tree;
 
 // 'class' does not need a CLSCompliant attribute because the assembly does not have a CLSCompliant attribute
 @namespace{Rellor.Core}
 
 public
-constantExpr[IScope scope] returns [int? value, System.Func<int> future, string failureReason]
-@init { this.scope = $scope; }
+constantExpr returns [CompileTimeValue value]
 	:	e=expr
-		{$future = $e.future; $value = ($future == null) ? (int?)$e.value : null; $failureReason = null;}
+		{$value = $e.value;}
 	;
 	catch [NotConstantException nce]
 	{
-		$value = null; $future = null; $failureReason = nce.Message;
+		LogNotConstantError((RellorToken)nce.Token, nce.Message);
+		$value = null;
 	}
 
-expr returns [int value, System.Func<int> future]
+expr returns [CompileTimeValue value]
 @init { bool match = false, neg = false; }
-@after { if ($future == null) $value = ConstrainValue($value); }
+@after { if ($value.NumericValue != null) $value = ConstrainValue($value); }
 	:	id=ID
-		{ResolveSymbol($id, out $value, out $future);}
+		{$value = ResolveSymbol($id);}
 	|	i=INT
-		{$value = ((NumberTree)$i).Number;}
+		{$value = CompileTimeValue.FromInt32(((NumberTree)$i).Number);}
 	|	^(HASH id=ID)
-		{ResolveSysConst($id, out $value, out $future);}
+		{$value = ResolveSysConst($id);}
 	|	^(HASHHASH id=ID)
-		{ResolveAction($id, out $value, out $future);}
+		{$value = ResolveAction($id);}
 	|	^(hd=HASHDOLLAR id=ID)
-		{ResolveHashDollar($hd, $id, out $value, out $future);}
+		{$value = ResolveHashDollar($hd, $id);}
 	|	str=SQ_STRING
-		{ResolveSqString($str, out $value, out $future);}
+		{$value = ResolveSqString($str);}
 	|	str=DQ_STRING
-		{ResolveDqString($str, out $value, out $future);}
+		{$value = ResolveDqString($str);}
 	|	^(UMINUS e=pexpr)
-		{$value = -$e.value;}
+		{$value = CompileTimeValue.FromInt32(-$e.value);}
 	|	^('+' l=pexpr r=pexpr)
-		{$value = $l.value + $r.value;}
+		{$value = CompileTimeValue.FromInt32($l.value + $r.value);}
 	|	^('-' l=pexpr r=pexpr)
-		{$value = $l.value - $r.value;}
+		{$value = CompileTimeValue.FromInt32($l.value - $r.value);}
 	|	^('*' l=pexpr r=pexpr)
-		{$value = $l.value * $r.value;}
+		{$value = CompileTimeValue.FromInt32($l.value * $r.value);}
 	|	^('/' l=pexpr r=pexpr)
-		{CheckDivByZero($r.value, $r.start);
-		 $value = $l.value / $r.value;}
+		{$value = CompileTimeValue.FromInt32(CheckDivByZero($r.value, $r.start) ? $l.value / $r.value : 0);}
 	|	^('%' l=pexpr r=pexpr)
-		{CheckDivByZero($r.value, $r.start);
-		 $value = $l.value \% $r.value;}
+		{$value = CompileTimeValue.FromInt32(CheckDivByZero($r.value, $r.start) ? $l.value \% $r.value : 0);}
 	|	^('&' l=pexpr r=pexpr)
-		{$value = $l.value & $r.value;}
+		{$value = CompileTimeValue.FromInt32($l.value & $r.value);}
 	|	^('|' l=pexpr r=pexpr)
-		{$value = $l.value | $r.value;}
+		{$value = CompileTimeValue.FromInt32($l.value | $r.value);}
 	|	^('~' e=pexpr)
-		{$value = ~$e.value;}
+		{$value = CompileTimeValue.FromInt32(~$e.value);}
 	|	^('&&' l=pexpr r=pexpr)
-		{$value = ($l.value != 0 && $r.value != 0) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($l.value != 0 && $r.value != 0) ? 1 : 0);}
 	|	^('||' l=pexpr r=pexpr)
-		{$value = ($l.value != 0 || $r.value != 0) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($l.value != 0 || $r.value != 0) ? 1 : 0);}
 	|	^('~~' e=pexpr)
-		{$value = ($e.value == 0) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($e.value == 0) ? 1 : 0);}
 	|	^(	('=='|'~=' {neg = true;})
 			{match = false;}
 			l=pexpr
 					)+
 				)
 			)
-			{$value = (match == neg) ? 0 : 1;}
+			{$value = CompileTimeValue.FromInt32((match == neg) ? 0 : 1);}
 		)
 	|	^('<' l=pexpr r=pexpr)
-		{$value = ($l.value < $r.value) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($l.value < $r.value) ? 1 : 0);}
 	|	^('>' l=pexpr r=pexpr)
-		{$value = ($l.value > $r.value) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($l.value > $r.value) ? 1 : 0);}
 	|	^('<=' l=pexpr r=pexpr)
-		{$value = ($l.value <= $r.value) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($l.value <= $r.value) ? 1 : 0);}
 	|	^('>=' l=pexpr r=pexpr)
-		{$value = ($l.value >= $r.value) ? 1 : 0;}
+		{$value = CompileTimeValue.FromInt32(($l.value >= $r.value) ? 1 : 0);}
 	|	^(nc=non_constant_op .*)
 		{NotConstant($nc.start, $nc.desc);}
 	;
 		|	INDIV_METHOD_CALL
 		|	FUNCTION_CALL
 		)
-		{$desc = "function call";}
+		{$desc = "a function call";}
     |	(	COMMON_PROP_ASSIGN
 		|	INDIV_PROP_ASSIGN
 		|	BYTE_ARRAY_ASSIGN
 		|	WORD_ARRAY_ASSIGN
 		|	'='
 		)
-		{$desc = "assignment";}
+		{$desc = "'='";}
     |	(	COMMON_PROP_PREINC
 		|	INDIV_PROP_PREINC
 		|	BYTE_ARRAY_PREINC
 		|	WORD_ARRAY_POSTINC
 		|	POSTINC
 		)
-		{$desc = "increment";}
+		{$desc = "'++'";}
     |	(	COMMON_PROP_PREDEC
 		|	BYTE_ARRAY_PREDEC
 		|	WORD_ARRAY_PREDEC
 		|	WORD_ARRAY_POSTDEC
 		|	POSTDEC
 		)
-		{$desc = "decrement";}
+		{$desc = "'--'";}
 	|	','|HAS|HASNT|IN|NOTIN|PROVIDES|OFCLASS|'.&'|'.#'|'..&'|'..#'
-	|	'.'|'..'|'::'
+	|	'.'|'..'|'::'|ARROW|DARROW
 	;
 
 pexpr returns [int value]
 	:	e=expr
-		{if ($e.future == null) $value = $e.value; else NotConstant($e.start, "an operation on a backpatched value");}
+		{if ($e.value.NeedsBackpatch == false) $value = (int)$e.value.NumericValue; else NotConstant($e.start, "an operation on a backpatched value");}
 	;

File Rellor.Core/EvalConstant.g3.cs

 using System;
 using Antlr.Runtime;
 using Antlr.Runtime.Tree;
+using Rellor.Core.Token;
+using System.Diagnostics.Contracts;
 
 namespace Rellor.Core
 {
-    partial class EvalConstant
+    partial class EvalConstant : ILogSink
     {
-        private IScope scope;
+        private MsgLogger<CoreMessages> logger;
+
+        public ILogSink LogSink { get; set; }
+        public IScope Scope { get; set; }
+
+        partial void OnCreated()
+        {
+            logger = new MsgLogger<CoreMessages>(this, "RE");
+        }
 
         private class NotConstantException : Exception
         {
             public IToken Token { get; private set; }
         }
 
-        private void CheckDivByZero(int value, CommonTree tree)
+        private bool CheckDivByZero(int value, CommonTree tree)
+        {
+            if (value == 0)
+            {
+                logger.Log(CoreMessages.DivisionByZero, (RellorToken)tree.Token);
+                return false;
+            }
+
+            return true;
+        }
+
+        private CompileTimeValue ConstrainValue(CompileTimeValue value)
+        {
+            // XXX implement target-specific truncation (i.e. mask to short int for Z-code)
+            return value;
+        }
+
+        private CompileTimeValue ResolveSymbol(CommonTree id)
+        {
+            var name = id.Text;
+            var symbol = Scope.Resolve(name);
+            return symbol.Value;
+        }
+
+        private CompileTimeValue ResolveSysConst(CommonTree id)
         {
             throw new NotImplementedException();
         }
 
-        private int ConstrainValue(int value)
+        private CompileTimeValue ResolveAction(CommonTree id)
         {
             throw new NotImplementedException();
         }
 
-        private void ResolveSymbol(CommonTree id, out int value, out Func<int> future)
+        private CompileTimeValue ResolveHashDollar(CommonTree hashDollar, CommonTree id)
         {
             throw new NotImplementedException();
         }
 
-        private void ResolveSysConst(CommonTree id, out int value, out Func<int> future)
+        private CompileTimeValue ResolveSqString(CommonTree str)
         {
             throw new NotImplementedException();
         }
 
-        private void ResolveAction(CommonTree id, out int value, out Func<int> future)
+        private CompileTimeValue ResolveDqString(CommonTree str)
         {
-            throw new NotImplementedException();
-        }
-
-        private void ResolveHashDollar(CommonTree hashDollar, CommonTree id, out int value, out Func<int> future)
-        {
-            throw new NotImplementedException();
-        }
-
-        private void ResolveSqString(CommonTree str, out int value, out Func<int> future)
-        {
-            throw new NotImplementedException();
-        }
-
-        private void ResolveDqString(CommonTree str, out int value, out Func<int> future)
-        {
-            throw new NotImplementedException();
+            return CompileTimeValue.FromString(Inform6Parser.ParseDqString(str.Text));
         }
 
         private void NotConstant(CommonTree position, string offender)
         {
-            throw new NotImplementedException();
+            Contract.Requires(position != null);
+            Contract.Requires(!string.IsNullOrEmpty(offender));
+
+            throw new NotConstantException(position.Token, offender);
         }
+
+        private void LogNotConstantError(RellorToken position, string failureReason)
+        {
+            Contract.Requires(position != null);
+            Contract.Requires(!string.IsNullOrEmpty(failureReason));
+
+            LogNotConstantError(this.logger, position, failureReason);
+        }
+
+        public static void LogNotConstantError(MsgLogger<CoreMessages> logger, RellorToken position, string failureReason)
+        {
+            Contract.Requires(logger != null);
+            Contract.Requires(position != null);
+            Contract.Requires(!string.IsNullOrEmpty(failureReason));
+
+            logger.Log(CoreMessages.ExprNotConstant, position);
+            logger.Log(CoreMessages.ExprNotConstant_Reason, position, failureReason);
+        }
+
+        #region ILogSink Members
+
+        void ILogSink.LogMessage(LogLevel level, string text, bool count)
+        {
+            var ls = this.LogSink;
+            if (ls != null)
+                ls.LogMessage(level, text, count);
+        }
+
+        void ILogSink.CountSuppressedMessage(LogLevel level)
+        {
+            var ls = this.LogSink;
+            if (ls != null)
+                ls.CountSuppressedMessage(level);
+        }
+
+        #endregion
     }
 }

File Rellor.Core/Inform6.g3

 	;
 
 constant_def
+@init { Symbol constSymbol = null; }
 	:	id
 		(	EQ? arg_expr
-			{DefineSymbol($Symbols::scope, SymbolType.Constant, $id.start, $arg_expr.tree);}
+			{constSymbol = DefineSymbol($Symbols::scope, SymbolType.Constant, $id.start, $arg_expr.tree);}
 			-> ^(id arg_expr)
 		|	/* nada */
-			{DefineSymbol($Symbols::scope, SymbolType.Constant, $id.start, new NumberTree(INT, 1));}
+			{constSymbol = DefineSymbol($Symbols::scope, SymbolType.Constant, $id.start, new NumberTree(INT, 1));}
 			-> id
 		)
+		{SetConstantValue(constSymbol, $Symbols::scope);}
 	;
 
 /**************************** STATEMENTS ****************************/
 
 simple_constant
     :	number
-    |	id
+    |	id				{ ReferenceSymbol($Symbols::scope, $program::globalScope, $id.start); }
 	|	HASH^ id
 	|	HASHHASH^ id
     |	HASHDOLLAR^

File Rellor.Core/Inform6.g3.parser.cs

             return (int)str[1];
         }
 
-        private static string ParseDqString(string str)
+        public static string ParseDqString(string str)
         {
             Contract.Requires(str != null);
             Contract.Requires(str.Length >= 2);
             return sb.ToString();
         }
 
-        private static string ParseSqString(string str)
+        public static string ParseSqString(string str)
         {
             Contract.Requires(str != null);
             Contract.Requires(str.Length >= 2);
                 }
                 else
                 {
+                    System.Diagnostics.Debug.Assert(
+                        existing.Type == SymbolType.PresumedConstant,
+                        "symbol defined after first reference is of type " + existing.Type + ", should be PresumedConstant");
+
+                    existing.Type = type;
                     existing.Defined = true;
                     existing.DefiningTree = definition;
                 }
                 onFailure();
         }
 
+        private void ReferenceSymbol(IScope resolveScope, IScope defineScope, RellorToken token)
+        {
+            Contract.Requires(resolveScope != null);
+            Contract.Requires(defineScope != null);
+            Contract.Requires(token != null);
+
+            var name = token.Text;
+            var symbol = resolveScope.Resolve(name);
+
+            if (symbol != null)
+            {
+                if (symbol.FirstReference == null)
+                {
+                    symbol.FirstReference = token;
+                    symbol.Referenced = true;
+                }
+            }
+            else
+            {
+                symbol = new Symbol()
+                {
+                    Name = name,
+                    Referenced = true,
+                    FirstReference = token,
+                    Type = SymbolType.PresumedConstant,
+                };
+                symbol.Value = new CompileTimeValue<Symbol>(BackpatchType.Symbol, symbol, CompileTimeType.Unknown);
+
+                defineScope.Define(name, symbol);
+            }
+        }
+
+        private void SetConstantValue(Symbol constSymbol, IScope scope)
+        {
+            var adaptor = new RellorTreeAdaptor();
+            var nodeStream = new CommonTreeNodeStream(adaptor, constSymbol.DefiningTree);
+            var eval = new EvalConstant(nodeStream)
+            {
+                LogSink = this,
+                Scope = scope,
+            };
+
+            var result = eval.constantExpr();
+
+            if (result != null)
+            {
+                constSymbol.Value = result;
+            }
+            else
+            {
+                // error was already logged, use a dummy value
+                constSymbol.Value = CompileTimeValue.FromInt32(0);
+            }
+        }
+
         protected override object GetMissingSymbol(IIntStream input,
                                        RecognitionException e,
                                        int expectedTokenType,
         {
             Contract.Ensures(Contract.Result<object>() is RellorToken);
 
-            string tokenText = null;
+            string tokenText;
             if (expectedTokenType == TokenTypes.EndOfFile)
                 tokenText = "<missing EOF>";
             else
                 tokenText = "<missing " + TokenNames[expectedTokenType] + ">";
-            RellorToken t = new RellorToken(expectedTokenType, tokenText);
+
             IToken current = ((ITokenStream)input).LT(1);
             if (current.Type == TokenTypes.EndOfFile)
+                current = ((ITokenStream)input).LT(-1);
+
+            RellorToken t = new RellorToken(expectedTokenType, tokenText)
             {
-                current = ((ITokenStream)input).LT(-1);
-            }
-            t.Line = current.Line;
-            t.CharPositionInLine = current.CharPositionInLine;
-            t.Channel = DefaultTokenChannel;
-            t.InputStream = current.InputStream;
+                Line = current.Line,
+                CharPositionInLine = current.CharPositionInLine,
+                Channel = DefaultTokenChannel,
+                InputStream = current.InputStream,
+            };
+
             return t;
         }
 

File Rellor.Core/Messages.cs

         MisplacedOrOperator,
         [Msg(4505, LogLevel.Protip, "did you mean '||'?", Continues = 4504)]
         MisplacedOrOperator_Protip,
+        [Msg(4506, LogLevel.Error, "division by zero")]
+        DivisionByZero,
     }
 
     [ContractClass(typeof(ILogSinkContract))]

File Rellor.Core/Rellor.Core.csproj

   </ItemGroup>
   <ItemGroup>
     <Compile Include="Analyzer.cs" />
+    <Compile Include="CompileTimeType.cs" />
+    <Compile Include="CompileTimeValue.cs" />
     <Compile Include="EvalConstant.g3.cs">
       <DependentUpon>EvalConstant.g3</DependentUpon>
     </Compile>

File Rellor.Core/Symbol.cs

 {
     public enum SymbolType
     {
+        [Description("presumed constant")]
+        PresumedConstant,
         Constant,
         Object,
         Class,
         public ITree DefiningTree { get; set; }
         public bool Defined { get; set; }
         public bool Referenced { get; set; }
-        public object Value { get; set; }
-        public Func<int> Future { get; set; }
+        public CompileTimeValue Value { get; set; }
+
+        public override string ToString()
+        {
+            return string.Format("{0} ({1})", this.Name ?? "<null>", this.Type);
+        }
     }
 }