-
Notifications
You must be signed in to change notification settings - Fork 0
/
Interpreter.cs
138 lines (123 loc) · 4.25 KB
/
Interpreter.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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace cslox
{
class Interpreter : IVisit<object>
{
public void Interpret(Expr expr)
{
try
{
object value = Evaluate(expr);
Console.WriteLine(Stringify(value));
}
catch (RuntimeError error)
{
Lox.RuntimeError(error);
}
}
public object Visit(Expr.Binary expr)
{
object left = Evaluate(expr.left);
object right = Evaluate(expr.right);
switch (expr.oper.type) {
case TokenType.BANG_EQUAL:
return !IsEqual(left, right);
case TokenType.EQUAL_EQUAL:
return IsEqual(left, right);
case TokenType.GREATER:
CheckNumberOperands(expr.oper, left, right);
return (double)left > (double)right;
case TokenType.GREATER_EQUAL:
CheckNumberOperands(expr.oper, left, right);
return (double)left >= (double)right;
case TokenType.LESS:
CheckNumberOperands(expr.oper, left, right);
return (double)left < (double)right;
case TokenType.LESS_EQUAL:
CheckNumberOperands(expr.oper, left, right);
return (double)left <= (double)right;
case TokenType.MINUS:
CheckNumberOperands(expr.oper, left, right);
return (double)left - (double)right;
case TokenType.PLUS:
if (left is double && right is double) {
return (double)left + (double)right;
}
if (left is string && right is string) {
return (String)left + (String)right;
}
throw new RuntimeError(expr.oper, "Operands must be two numbers or two strings.");
case TokenType.SLASH:
CheckNumberOperands(expr.oper, left, right);
return (double)left / (double)right;
case TokenType.STAR:
CheckNumberOperands(expr.oper, left, right);
return (double)left * (double)right;
}
// Unreachable.
return null;
}
public object Visit(Expr.Grouping expr)
{
return Evaluate(expr.expr);
}
public object Visit(Expr.Literal expr)
{
return expr.value;
}
public object Visit(Expr.Unary expr)
{
object right = Evaluate(expr.right);
switch (expr.oper.type) {
case TokenType.BANG:
return !IsTruthy(right);
case TokenType.MINUS:
CheckNumberOperand(expr.oper, right);
return -(double)right;
}
// Unreachable.
return null;
}
private void CheckNumberOperand(Token oper, object operand)
{
if (!(operand is double))
{
throw new RuntimeError(oper, "Operand must be a number.");
}
}
private void CheckNumberOperands(Token oper, object left, object right)
{
if (!(left is double && right is double))
{
throw new RuntimeError(oper, "Operands must be numbers.");
}
}
private object Evaluate(Expr expr)
{
return expr.Accept(this);
}
private bool IsTruthy(object obj)
{
if (obj is null) return false;
if (obj is bool) return (bool)obj;
return true;
}
private bool IsEqual(object a, object b)
{
// nil is only equal to nil
if (a == null && b == null) return true;
if (a == null) return false;
return a.Equals(b);
}
private string Stringify(object obj)
{
if (obj == null) return "nil";
return obj.ToString();
}
}
}