-
Notifications
You must be signed in to change notification settings - Fork 0
/
Lox.cs
229 lines (207 loc) · 8.76 KB
/
Lox.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
using System;
using System.Collections.Generic;
using System.Linq;
using LSharp.Interpreter;
using static System.Console;
namespace LSharp
{
class Lox
{
private static readonly Interpreter.Interpreter interpreter = new Interpreter.Interpreter();
private static bool HadErrors = false;
private static bool HadRuntimeError = false;
private static string EntryPointName;
static void Main(string[] args)
{
if (args.Length > 1)
{
WriteLine("Usage: ls [script]");
Environment.Exit(1);
}
else if (args.Length == 1)
{
RunFile(args[0]);
}
else
{
RunPrompt();
}
}
/// <summary>
/// Reads the content of a LS or Lox file and proceeds to apply the scanning, parsing and
/// static analysis on the code. Returns a list of tokens ready to be executed by the interpreter.
/// </summary>
/// <param name="path">The path of the file to be resolved.</param>
public static List<Stmt> ResolveFile(string path)
{
var source = System.IO.File.ReadAllText(path);
var scaner = new LSharp.Scanner.Scanner(source);
var tokens = scaner.ScanTokens();
var parser = new LSharp.Parser.Parser(tokens);
var statements = parser.Parse();
if (HadErrors) return null;
var resolver = new Resolver(interpreter);
resolver.Resolve(statements);
if (HadErrors) return null;
return statements;
}
///<summary>
/// Method intended to parse a file if LS is executed with a path to a lox file as a parameter.
///</summary>
private static void RunFile(string path)
{
var source = System.IO.File.ReadAllText(path);
EntryPointName = path.Split("/").Last();
var fileExtension = EntryPointName.Split(".").Last();
if (fileExtension != "ls" && fileExtension != "lox")
{
WriteLine("Unsupported file. Please, provide a path to a valid L# file (a file with the .ls or .lox extension).");
Environment.Exit(1);
}
Run(source);
if (HadErrors) Environment.Exit(1);
if (HadRuntimeError) Environment.Exit(1);
}
/// <summary>
/// Method intended to be executed if the interpreter is used via REPL.
/// </summary>
private static void RunPrompt()
{
WriteLine("Welcome to L# REPL\nTo exit press Ctrl+C, or type 'exit'");
for (; ; )
{
Write(">");
var line = ReadLine();
if (line == "exit") break;
Run(line);
HadErrors = false;
}
}
/// <summary>
/// Method intended to begin the execution of the code, by providing the received text input (from both a file or REPL)
/// to the Scanner.
/// </summary>
/// <param name="source">The string that represents the source code of our app. Not nullable.</param>
private static void Run(string source)
{
var scaner = new LSharp.Scanner.Scanner(source);
var tokens = scaner.ScanTokens();
var parser = new LSharp.Parser.Parser(tokens, EntryPointName);
var statements = parser.Parse();
if (HadErrors) return;
var resolver = new Resolver(interpreter);
resolver.Resolve(statements);
if (HadErrors) return;
interpreter.Interpret(statements, EntryPointName);
}
/// <summary>
/// Method intended to provide error information to the Report method. It can be percived as a decorator around the
/// report method.
/// </summary>
/// <param name="line">Line number where the error was found.</param>
/// <param name="message">Message intended to notify a user about its errors.</param>
public static void Error(int line, string message)
{
Report(line, "", message);
}
/// <summary>
/// Reports a runtime error to the error.
/// </summary>
/// <param name="error">Runtime error produced by the interpreter.</param>
public static void RuntimeError(LSharp.Interpreter.RuntimeError error)
{
var fileName = error.FileName != null ? $"{error.FileName} - " : "";
WriteLine($"{error.Message}\n[{fileName}line {error.token.Line}]");
HadRuntimeError = true;
}
/// <summary>
/// Method intended to write to the console error messages.
/// </summary>
/// <param name="line">Line number where the error was found.</param>
/// <param name="where">Scope where the error took place.</param>
/// <param name="message">Message intented to nofity the user about its errors.</param>
private static void Report(int line, string where, string message)
{
WriteLine($"[line {line}] Error {where}: {message}");
HadErrors = true;
}
/// <summary>
/// Method intended to write to the console error messages.
/// </summary>
/// <param name="line">Line number where the error was found.</param>
/// <param name="where">Scope where the error took place.</param>
/// <param name="message">Message intented to nofity the user about its errors.</param>
/// <param name="fileName">The name of the file where the error took place.</param>
private static void Report(int line, string where, string message, string fileName)
{
WriteLine($"[{fileName} - line {line}] Error {where}: {message}");
HadErrors = true;
}
/// <summary>
/// Reports a syntax error. Returns a formatted message that includes the token that originated the error, alonside
/// the line number where the error was detected.
/// </summary>
/// <param name="token">Token that produced a syntax error.</param>
/// <param name="message">Message intended to describe the error.</param>
public static void Error(Tokens.Token token, string message)
{
if (token.Type == Tokens.TokenType.EOF)
{
Report(token.Line, "at end", message);
}
else
{
Report(token.Line, $"at '{token.Lexeme}'", message);
}
}
/// <summary>
/// Reports a syntax error. Returns a formatted message that includes the token that originated the error, alonside
/// the line number and the name of the file where the error was detected.
/// </summary>
/// <param name="token">Token that produced a syntax error.</param>
/// <param name="message">Message intended to describe the error.</param>
public static void Error(Tokens.Token token, string message, string fileName)
{
if (token.Type == Tokens.TokenType.EOF)
{
Report(token.Line, "at end", message, fileName);
}
else
{
Report(token.Line, $"at '{token.Lexeme}'", message, fileName);
}
}
}
class Tester
{
static void Main(string[] args)
{
var expression = new Expression.Binary(
new Expression.Unary(
new Tokens.Token(Tokens.TokenType.MINNUS, "-", null, 1),
new Expression.Literal(123)
),
new Expression.Grouping(new Expression.Literal(48.67)),
new Tokens.Token(Tokens.TokenType.STAR, "*", null, 1));
var expressionTwo = new Expression.Binary(
new Expression.Grouping(
new Expression.Binary(
new Expression.Literal(1),
new Expression.Literal(2),
new Tokens.Token(Tokens.TokenType.PLUS, "+", null, 1)
)
),
new Expression.Grouping(
new Expression.Binary(
new Expression.Literal(4),
new Expression.Literal(5),
new Tokens.Token(Tokens.TokenType.MINNUS, "-", null, 1)
)
),
new Tokens.Token(Tokens.TokenType.STAR, "*", null, 1)
);
WriteLine(new AstPrinter().Visit(expression));
WriteLine(new RPNPrinter().Visit(expressionTwo));
}
}
}