Jelajahi Sumber

Preprocessor fixes

Bepsi 6 tahun lalu
induk
melakukan
7fa0386937

+ 24 - 3
NTERA.Compiler/Compiler.cs

@@ -90,6 +90,7 @@ namespace NTERA.Compiler
 			"LEFT", //special casing for __FORMAT
 			"RIGHT", //special casing for __FORMAT
 			"DEBUG_MODE",
+			"NOITEM",
 		};
 
 		protected static string[] DefaultGlobalStringVariables =
@@ -164,11 +165,22 @@ namespace NTERA.Compiler
 			var funcs = new List<FunctionDefinition>
 			{
 				new FunctionDefinition("ABS", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("SQRT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("SIGN", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("CSVCALLNAME", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("TOINT", new[] { new FunctionParameter("a", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("RAND", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("VARSIZE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("TOSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("b", new string[0], "") }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("TOLOWER", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("TOHALF", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("TOFULL", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("TOUPPER", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("ISNUMERIC", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("LOG10", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("ESCAPE", new[] { new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("STRCOUNT", new[] { new FunctionParameter("input", new string[0]), new FunctionParameter("match", new string[0]) }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("MIN", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("MAX", new[] { new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
@@ -186,13 +198,18 @@ namespace NTERA.Compiler
 				new FunctionDefinition("CSVNAME", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("CSVBASE", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("CSVTALENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("CSVCFLAG", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("CSVRELATION", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("CSVCSTR", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("CSVEXP", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("CSVABL", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("GETNUM", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("FINDCHARA", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("LIMIT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("SUMCARRAY", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("MAXCARRAY", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("SUBSTRINGU", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
+				new FunctionDefinition("FINDELEMENT", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0]),new FunctionParameter("a", new string[0]),new FunctionParameter("a", new string[0]), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("GROUPMATCH", new[] { new FunctionParameter("a", new string[0]), new FunctionParameter("a", new string[0], isArrayParameter: true), }, new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("GETTIME", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
 				new FunctionDefinition("SAVENOS", new FunctionParameter[0], new FunctionVariable[0], true, "_GLOBAL", new Marker()),
@@ -215,10 +232,14 @@ namespace NTERA.Compiler
 				new Keyword("PRINTFORMLC", true, true),
 				new Keyword("PRINTFORM", true, true),
 				new Keyword("PRINTPLAINFORM", true, true),
+				new Keyword("DEBUGPRINTFORM", true, true),
 				new Keyword("DEBUGPRINTFORML", true, true),
 				new Keyword("THROW", true, true),
 
 				new Keyword("PRINT", true),
+				new Keyword("PRINTD", true),
+				new Keyword("PRINTDW", true),
+				new Keyword("PRINTDL", true),
 				new Keyword("PRINTW", true),
 				new Keyword("PRINTV", true),
 				new Keyword("PRINTL", true),
@@ -297,7 +318,7 @@ namespace NTERA.Compiler
 			{
 				try
 				{
-					foreach (var kv in Preprocessor.PreprocessCodeFile(File.ReadAllText(file), Path.GetFileName(file), null))
+					foreach (var kv in Preprocessor.PreprocessCodeFile(File.ReadAllText(file), Path.GetFileName(file), preprocessedConstants.ToArray()))
 						preprocessedFunctions[kv.Key] = kv.Value;
 				}
 				catch (Exception ex)
@@ -332,9 +353,9 @@ namespace NTERA.Compiler
 				{
 					var locals = new VariableDictionary();
 					foreach (var param in kv.Key.Variables)
-						locals[param.Name] = param.DefaultValue ?? param.ValueType == Interpreter.ValueType.String ? new Value("") : new Value(0d);
+						locals[param.Name] = param.CalculatedValue;
 
-					Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition);
+					Parser parser = new Parser(kv.Value, kv.Key, funcs, procedures, globals, locals, stringStatements, csvDefinition, preprocessedConstants.ToArray());
 
 					var nodes = parser.Parse(out var localErrors, out var localWarnings);
 

+ 2 - 0
NTERA.Interpreter/Compiler/FunctionDefinition.cs

@@ -51,6 +51,8 @@ namespace NTERA.Interpreter.Compiler
 
 		public Value? DefaultValue { get; }
 
+		public Value CalculatedValue => DefaultValue ?? ValueType == ValueType.String ? new Value("") : new Value(0d);
+
 		public FunctionVariable(string name, ValueType valueType, VariableType variableType = VariableType.None, Value? defaultValue = null)
 		{
 			Name = name;

+ 19 - 9
NTERA.Interpreter/Compiler/Parser.cs

@@ -13,6 +13,7 @@ namespace NTERA.Interpreter.Compiler
 
 		protected ICollection<FunctionDefinition> FunctionDefinitions { get; }
 		protected ICollection<FunctionDefinition> ProcedureDefinitions { get; }
+		protected ICollection<FunctionVariable> ConstantDefinitions { get; }
 
 		protected List<ParserError> Errors { get; } = new List<ParserError>();
 		protected List<ParserError> Warnings { get; } = new List<ParserError>();
@@ -48,7 +49,7 @@ namespace NTERA.Interpreter.Compiler
 			Lexer.TokenMarker.Line + SelfDefinition.Position.Line - 1,
 			Lexer.TokenMarker.Column);
 
-		public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, VariableDictionary globalVariables, VariableDictionary localVariables, ICollection<Keyword> explicitKeywords, CSVDefinition csvDefinition)
+		public Parser(string input, FunctionDefinition selfDefinition, ICollection<FunctionDefinition> functionDefinitions, ICollection<FunctionDefinition> procedureDefinitions, VariableDictionary globalVariables, VariableDictionary localVariables, ICollection<Keyword> explicitKeywords, CSVDefinition csvDefinition, ICollection<FunctionVariable> constantDefnitions)
 		{
 			Lexer = new Lexer(input);
 			Enumerator = Lexer.GetEnumerator();
@@ -56,6 +57,7 @@ namespace NTERA.Interpreter.Compiler
 			SelfDefinition = selfDefinition;
 			FunctionDefinitions = functionDefinitions;
 			ProcedureDefinitions = procedureDefinitions;
+			ConstantDefinitions = constantDefnitions;
 			GlobalVariables = globalVariables;
 			LocalVariables = localVariables;
 			ExplicitKeywords = explicitKeywords;
@@ -117,10 +119,19 @@ namespace NTERA.Interpreter.Compiler
 				case Token.Identifer:
 
 					if (GlobalVariables.ContainsKey(Lexer.Identifier)
-						|| LocalVariables.ContainsKey(Lexer.Identifier))
+						|| LocalVariables.ContainsKey(Lexer.Identifier)
+						|| ConstantDefinitions.Any(x => x.Name.Equals(Lexer.Identifier, StringComparison.OrdinalIgnoreCase)))
 					{
 						string variableName = Lexer.Identifier;
-						bool isGlobal = GlobalVariables.ContainsKey(variableName);
+
+						ValueType type = (ValueType)0;
+
+						if (GlobalVariables.ContainsKey(variableName))
+							type = GlobalVariables[variableName].Type;
+						else if (LocalVariables.ContainsKey(variableName))
+							type = LocalVariables[variableName].Type;
+						else if (ConstantDefinitions.Any(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)))
+							type = ConstantDefinitions.First(x => x.Name.Equals(variableName, StringComparison.OrdinalIgnoreCase)).ValueType;
 
 						var node = new ExecutionNode
 						{
@@ -167,10 +178,6 @@ namespace NTERA.Interpreter.Compiler
 						}
 						else
 						{
-							var type = isGlobal
-								? GlobalVariables[variableName].Type
-								: LocalVariables[variableName].Type;
-
 							value = type == ValueType.String
 								? ParseString(out error, true, true)
 								: Expression(out error);
@@ -302,6 +309,7 @@ namespace NTERA.Interpreter.Compiler
 					}
 					else if (Lexer.Identifier.Equals("CALLFORM", StringComparison.OrdinalIgnoreCase)
 							 || Lexer.Identifier.Equals("TRYCALLFORM", StringComparison.OrdinalIgnoreCase)
+							 || Lexer.Identifier.Equals("TRYCCALLFORM", StringComparison.OrdinalIgnoreCase)
 							 || Lexer.Identifier.Equals("TRYJUMPFORM", StringComparison.OrdinalIgnoreCase))
 
 					{
@@ -555,7 +563,8 @@ namespace NTERA.Interpreter.Compiler
 					}
 
 					if (GlobalVariables.ContainsKey(Lexer.Identifier)
-						|| LocalVariables.ContainsKey(Lexer.Identifier))
+						|| LocalVariables.ContainsKey(Lexer.Identifier)
+						|| ConstantDefinitions.Any(x => x.Name == Lexer.Identifier))
 					{
 						var subNode = new ExecutionNode
 						{
@@ -775,7 +784,8 @@ namespace NTERA.Interpreter.Compiler
 				else if (token == Token.Identifer)
 				{
 					if (GlobalVariables.ContainsKey(Lexer.Identifier)
-						|| LocalVariables.ContainsKey(Lexer.Identifier))
+						|| LocalVariables.ContainsKey(Lexer.Identifier)
+						|| ConstantDefinitions.Any(x => x.Name == Lexer.Identifier))
 					{
 						operands.Push(GetVariable(out error));
 						if (error != null)

+ 67 - 65
NTERA.Interpreter/Compiler/Preprocessor.cs

@@ -18,7 +18,7 @@ namespace NTERA.Interpreter.Compiler
 				{
 					if (lexer.TokenMarker.Column != 1)
 						continue;
-					
+
 					if (enumerator.Current == Token.Sharp)
 					{
 						enumerator.MoveNext();
@@ -27,75 +27,75 @@ namespace NTERA.Interpreter.Compiler
 						{
 							case Token.Dims:
 							case Token.Dim:
+							{
+								bool isString = enumerator.Current != Token.Dim;
+
+								enumerator.MoveNext();
+								VariableType variableType = VariableType.None;
+
+								while (enumerator.Current == Token.Const
+									   || enumerator.Current == Token.Ref
+									   || enumerator.Current == Token.Dynamic
+									   || (enumerator.Current == Token.Identifer && lexer.Identifier.Equals("GLOBAL", StringComparison.OrdinalIgnoreCase))
+									   || (enumerator.Current == Token.Identifer && lexer.Identifier.Equals("CHARADATA", StringComparison.OrdinalIgnoreCase))
+									   || (enumerator.Current == Token.Identifer && lexer.Identifier.Equals("SAVEDATA", StringComparison.OrdinalIgnoreCase)))
 								{
-									bool isString = enumerator.Current != Token.Dim;
+									if (enumerator.Current == Token.Const)
+										variableType |= VariableType.Constant;
+									else if (enumerator.Current == Token.Ref)
+										variableType |= VariableType.Reference;
+									else if (enumerator.Current == Token.Dynamic)
+										variableType |= VariableType.Dynamic;
+									else if (enumerator.Current == Token.Identifer && lexer.Identifier.Equals("GLOBAL", StringComparison.OrdinalIgnoreCase))
+										variableType |= VariableType.SaveData;
+									else if (enumerator.Current == Token.Identifer && lexer.Identifier.Equals("CHARADATA", StringComparison.OrdinalIgnoreCase))
+										variableType |= VariableType.CharaData;
+									else if (enumerator.Current == Token.Identifer && lexer.Identifier.Equals("SAVEDATA", StringComparison.OrdinalIgnoreCase))
+										variableType |= VariableType.Global;
 
 									enumerator.MoveNext();
-									VariableType variableType = VariableType.None;
-
-									while (enumerator.Current == Token.Const
-										   || enumerator.Current == Token.Ref
-										   || enumerator.Current == Token.Dynamic
-										   || enumerator.Current == Token.SaveData
-										   || enumerator.Current == Token.CharaData
-										   || enumerator.Current == Token.Global)
-									{
-										if (enumerator.Current == Token.Const)
-											variableType |= VariableType.Constant;
-										else if (enumerator.Current == Token.Ref)
-											variableType |= VariableType.Reference;
-										else if (enumerator.Current == Token.Dynamic)
-											variableType |= VariableType.Dynamic;
-										else if (enumerator.Current == Token.SaveData)
-											variableType |= VariableType.SaveData;
-										else if (enumerator.Current == Token.CharaData)
-											variableType |= VariableType.CharaData;
-										else if (enumerator.Current == Token.Global)
-											variableType |= VariableType.Global;
-
-										enumerator.MoveNext();
-									}
+								}
 
-									string variable = lexer.Identifier;
+								string variable = lexer.Identifier;
 
-									enumerator.MoveNext();
-									Value? defaultValue = null;
+								enumerator.MoveNext();
+								Value? defaultValue = null;
 
-									if (enumerator.Current == Token.Comma)
+								if (enumerator.Current == Token.Comma)
+								{
+									while (enumerator.MoveNext()
+										   && enumerator.Current != Token.Equal
+										   && enumerator.Current != Token.NewLine
+										   && enumerator.Current != Token.EOF)
 									{
-										while (enumerator.MoveNext()
-											   && enumerator.Current != Token.Equal
-											   && enumerator.Current != Token.NewLine
-											   && enumerator.Current != Token.EOF)
-										{
-											//arraySize = (int)lexer.Expression().Real;
-
-											//the array size goes here, but we ignore it since it's useless to us
-										}
+										//arraySize = (int)lexer.Expression().Real;
+
+										//the array size goes here, but we ignore it since it's useless to us
 									}
+								}
 
-									if (enumerator.Current == Token.Equal)
-									{
-										enumerator.MoveNext();
+								if (enumerator.Current == Token.Equal)
+								{
+									enumerator.MoveNext();
 
-										defaultValue = ConstantExpression(lexer, constantDefinitions);
-									}
-									else if (enumerator.Current != Token.NewLine
-											 && enumerator.Current != Token.EOF)
-									{
-										throw new ParserException("Invalid function declaration", lexer.TokenMarker);
-									}
+									defaultValue = ConstantExpression(lexer, constantDefinitions);
+								}
+								else if (enumerator.Current != Token.NewLine
+										 && enumerator.Current != Token.EOF)
+								{
+									throw new ParserException("Invalid function declaration", lexer.TokenMarker);
+								}
 
-									var functionDefinition = new FunctionVariable(variable,
-										isString ? ValueType.String : ValueType.Real,
-										variableType,
-										defaultValue);
+								var functionDefinition = new FunctionVariable(variable,
+									isString ? ValueType.String : ValueType.Real,
+									variableType,
+									defaultValue);
 
-									constantDefinitions.Add(functionDefinition);
-									yield return functionDefinition;
+								constantDefinitions.Add(functionDefinition);
+								yield return functionDefinition;
 
-									break;
-								}
+								break;
+							}
 						}
 					}
 					else
@@ -208,9 +208,7 @@ namespace NTERA.Interpreter.Compiler
 							{
 								enumerator.MoveNext();
 
-								defaultValue = ConstantExpression(lexer);
-
-								//enumerator.MoveNext();
+								defaultValue = ConstantExpression(lexer, constantDefinitions);
 							}
 
 							if (enumerator.Current == Token.Comma
@@ -275,8 +273,8 @@ namespace NTERA.Interpreter.Compiler
 										//arraySize = (int)lexer.Expression().Real;
 
 										//the array size goes here, but we ignore it since it's useless to us
-										}
 									}
+								}
 
 								if (enumerator.Current == Token.Equal)
 								{
@@ -463,6 +461,9 @@ namespace NTERA.Interpreter.Compiler
 
 			void Operation(Token token)
 			{
+				if (stack.Count < 2)
+					throw new ParserException("Not enough operands to perform operation", lexer.TokenMarker);
+
 				Value b = stack.Pop();
 				Value a = stack.Pop();
 				Value result = a.Operate(b, token);
@@ -488,14 +489,12 @@ namespace NTERA.Interpreter.Compiler
 				}
 				else if (currentEnumerator.Current == Token.Identifer)
 				{
-					var variable = constantDefinitions?.FirstOrDefault(x => x.Name == lexer.Identifier && x.VariableType.HasFlag(VariableType.Constant));
+					var variable = constantDefinitions?.FirstOrDefault(x => x.Name.Equals(lexer.Identifier, StringComparison.OrdinalIgnoreCase) && x.VariableType.HasFlag(VariableType.Constant));
 
 					if (variable == null)
 						throw new ParserException("Undeclared variable " + lexer.Identifier, lexer.TokenMarker);
 
-					var value = variable.DefaultValue ?? variable.ValueType == ValueType.String ? (Value)"" : 0;
-
-					stack.Push(value);
+					stack.Push(variable.CalculatedValue);
 				}
 				else if (currentEnumerator.Current == Token.LParen)
 				{
@@ -531,6 +530,9 @@ namespace NTERA.Interpreter.Compiler
 			while (operators.Count > 0)
 				Operation(operators.Pop());
 
+			if (stack.Count == 0)
+				throw new ParserException("Empty expression", lexer.TokenMarker);
+
 			return stack.Pop();
 		}
 	}

+ 0 - 9
NTERA.Interpreter/Compiler/Token.cs

@@ -28,15 +28,6 @@
 		[LexerKeyword("DYNAMIC")]
 		Dynamic,
 
-		[LexerKeyword("SAVEDATA")]
-		SaveData,
-
-		[LexerKeyword("CHARADATA")]
-		CharaData,
-
-		[LexerKeyword("GLOBAL")]
-		Global,
-
 		[LexerKeyword("FUNCTION")]
 		[LexerKeyword("FUNCTIONS")]
 		ReturnFunction,