diff --git a/Parser/EvaluationScope.cs b/Parser/EvaluationScope.cs new file mode 100644 index 0000000..6eb7a77 --- /dev/null +++ b/Parser/EvaluationScope.cs @@ -0,0 +1,10 @@ +using Parser.Objects; +using System.Collections.Generic; + +namespace Parser +{ + internal class EvaluationScope + { + public Dictionary Variables { get; } = new Dictionary(); + } +} \ No newline at end of file diff --git a/Parser/Evaluator.cs b/Parser/Evaluator.cs index 8aeaa9a..74a1d6b 100644 --- a/Parser/Evaluator.cs +++ b/Parser/Evaluator.cs @@ -4,14 +4,10 @@ using Parser.Objects; using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.Linq; namespace Parser { - internal class EvaluationScope - { - public Dictionary Variables { get; } = new Dictionary(); - } - internal class Evaluator { private readonly SyntaxTree _syntaxTree; @@ -218,7 +214,59 @@ namespace Parser private MObject? EvaluateFunctionCall(FunctionCallExpressionSyntaxNode expression) { - throw new NotImplementedException(); + var arguments = new List(); + var nodes = expression.Nodes.Where(n => n.IsNode).Select(n => (ExpressionSyntaxNode)n.AsNode()!); + var allGood = true; + foreach (var node in nodes) + { + var argument = EvaluateExpression(node); + if (argument is null) + { + _diagnostics.ReportCannotEvaluateExpression( + new TextSpan(node.Position, node.FullWidth)); + allGood = false; + } + else + { + arguments.Add(argument); + } + + } + if (!allGood) + { + return null; + } + + var function = GetFunctionSymbol(expression.FunctionName); + if (function.Name == "disp") + { + return EvaluateDisp(arguments); + } + else + { + throw new NotImplementedException("Functions are not supported."); + } + } + + private MObject? EvaluateDisp(List arguments) + { + if (arguments.Count != 1) + { + throw new NotImplementedException($"Cannot evaluate disp() with {arguments.Count} arguments."); + } + + Console.WriteLine(arguments[0]); + return null; + } + + private FunctionSymbol GetFunctionSymbol(ExpressionSyntaxNode functionName) + { + if (functionName.Kind == TokenKind.IdentifierNameExpression) + { + return new FunctionSymbol(((IdentifierNameExpressionSyntaxNode)functionName).Text); + } + + throw new NotImplementedException($"Unknown function symbol '{functionName.Text}'."); } private MObject? EvaluateCellArrayElementAccess(CellArrayElementAccessExpressionSyntaxNode expression) @@ -248,7 +296,11 @@ namespace Parser private MObject? EvaluateStringLiteralExpression(StringLiteralSyntaxNode expression) { - throw new NotImplementedException(); + return expression.StringToken.Value switch + { + string s => MObject.CreateCharArray(s.ToCharArray()), + _ => null, + }; } private MObject? EvaluateNumberLiteralExpression(NumberLiteralSyntaxNode expression) diff --git a/Parser/FunctionSymbol.cs b/Parser/FunctionSymbol.cs new file mode 100644 index 0000000..eb43cf0 --- /dev/null +++ b/Parser/FunctionSymbol.cs @@ -0,0 +1,12 @@ +namespace Parser +{ + internal class FunctionSymbol + { + public string Name { get; } + + public FunctionSymbol(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/Parser/Objects/MCharArray.cs b/Parser/Objects/MCharArray.cs new file mode 100644 index 0000000..188e242 --- /dev/null +++ b/Parser/Objects/MCharArray.cs @@ -0,0 +1,22 @@ +namespace Parser.Objects +{ + public class MCharArray : MObject + { + private MCharArray(char[] chars) + { + Chars = chars; + } + + public char[] Chars { get; } + + public static MCharArray Create(char[] chars) + { + return new MCharArray(chars); + } + + public override string ToString() + { + return new string(Chars); + } + } +} \ No newline at end of file diff --git a/Parser/Objects/MObject.cs b/Parser/Objects/MObject.cs index cda575b..c76d9e6 100644 --- a/Parser/Objects/MObject.cs +++ b/Parser/Objects/MObject.cs @@ -6,5 +6,10 @@ { return MDoubleNumber.Create(value); } + + public static MCharArray CreateCharArray(char[] chars) + { + return MCharArray.Create(chars); + } } } \ No newline at end of file diff --git a/Solution.sln b/Solution.sln index b48c4d5..56fc479 100644 --- a/Solution.sln +++ b/Solution.sln @@ -21,7 +21,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Repl", "Repl\Repl.csproj", "{8FEDFE5D-3320-418F-88A6-09C1B51C4441}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MApplication", "MApplication\MApplication.csproj", "{A7EE271C-8822-43EA-BA13-5D6D5DC5B581}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MApplication", "MApplication\MApplication.csproj", "{A7EE271C-8822-43EA-BA13-5D6D5DC5B581}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cmi", "cmi\cmi.csproj", "{C2447F0B-733D-4755-A104-5B82E24D3F47}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -57,6 +59,10 @@ Global {A7EE271C-8822-43EA-BA13-5D6D5DC5B581}.Debug|Any CPU.Build.0 = Debug|Any CPU {A7EE271C-8822-43EA-BA13-5D6D5DC5B581}.Release|Any CPU.ActiveCfg = Release|Any CPU {A7EE271C-8822-43EA-BA13-5D6D5DC5B581}.Release|Any CPU.Build.0 = Release|Any CPU + {C2447F0B-733D-4755-A104-5B82E24D3F47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2447F0B-733D-4755-A104-5B82E24D3F47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2447F0B-733D-4755-A104-5B82E24D3F47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2447F0B-733D-4755-A104-5B82E24D3F47}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/cmi.cmd b/cmi.cmd new file mode 100644 index 0000000..6a05f9e --- /dev/null +++ b/cmi.cmd @@ -0,0 +1 @@ +dotnet run --project cmi -- %* diff --git a/cmi/Program.cs b/cmi/Program.cs new file mode 100644 index 0000000..ec8f17d --- /dev/null +++ b/cmi/Program.cs @@ -0,0 +1,47 @@ +using Parser; +using System; +using System.IO; + +namespace cmi +{ + class Program + { + static void Main(string[] args) + { + if (args.Length != 1) + { + Console.Error.WriteLine("Usage: cmi "); + } + + var fileName = args[0]; + var text = File.ReadAllText(fileName); + var tree = SyntaxTree.Parse(text); + var compilation = Compilation.Create(tree); + if (tree.Diagnostics.Diagnostics.Count > 0) + { + foreach (var diagnostic in tree.Diagnostics.Diagnostics) + { + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine($"{diagnostic.Span}: {diagnostic.Message}"); + Console.ResetColor(); + } + } + + var context = new CompilationContext(); + var evaluationResult = compilation.Evaluate(context); + + foreach (var diagnostic in evaluationResult.Diagnostics) + { + Console.ForegroundColor = ConsoleColor.DarkRed; + Console.WriteLine($"{diagnostic.Span}: {diagnostic.Message}"); + Console.ResetColor(); + } + + var result = evaluationResult.Value?.ToString(); + if (result is not null) + { + Console.WriteLine(result); + } + } + } +} diff --git a/cmi/cmi.csproj b/cmi/cmi.csproj new file mode 100644 index 0000000..7c25f37 --- /dev/null +++ b/cmi/cmi.csproj @@ -0,0 +1,14 @@ + + + + Exe + net5.0 + enable + preview + + + + + + + diff --git a/examples/helloworld/hello.m b/examples/helloworld/hello.m new file mode 100644 index 0000000..51c6bb1 --- /dev/null +++ b/examples/helloworld/hello.m @@ -0,0 +1,3 @@ +x = 1; +disp(x + 2); +disp('Hello world!');