Skip to content

Commit df17053

Browse files
committed
comments and formatting
1 parent 385089e commit df17053

16 files changed

+181
-120
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ golox/pkg/scanner/errorHandler.txt
1919
golox/pkg/scanner/scanner.txt
2020
golox/pkg/scanner/token.txt
2121
golox/pkg/scanner/tokentype.txt
22+
/main

.idea/.gitignore

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/dictionaries/reilandeubank.xml

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/lisp-interpreter.iml

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/interpreter/callable.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,31 @@ import (
66
"github.com/reilandeubank/golisp/pkg/parser"
77
)
88

9+
// LispCallable is the interface that LispFunction implements (unnecessary?)
910
type LispCallable interface {
1011
Arity() int
1112
Call(i *Interpreter, arguments []interface{}) (interface{}, error)
1213
String() string
1314
}
1415

16+
// LispFunction is the struct that all functions in the language are stored as
1517
type LispFunction struct {
16-
Declaration parser.FuncDefinition
17-
Closure *environment
18+
Declaration parser.FuncDefinition
19+
Closure *environment
1820
IsInitializer bool
1921
}
2022

23+
// String returns a string representation of the function for debugging purposes
2124
func (l LispFunction) String() string {
2225
return "<fn " + l.Declaration.Name.Lexeme + ">"
2326
}
2427

28+
// Arity is simply the number of parameters for a function (arity must match for function calls)
2529
func (l LispFunction) Arity() int {
2630
return len(l.Declaration.Params)
2731
}
2832

33+
// Call generates a new environment, defines each parameter as the passed arguments, then evaluates
2934
func (l LispFunction) Call(i *Interpreter, arguments []interface{}) (interface{}, error) {
3035
env := NewEnvironmentWithEnclosing(*l.Closure)
3136

@@ -34,4 +39,4 @@ func (l LispFunction) Call(i *Interpreter, arguments []interface{}) (interface{}
3439
}
3540

3641
return i.evaluateFunction(l.Declaration.Body, env)
37-
}
42+
}

pkg/interpreter/environment.go

+5-13
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import (
44
"github.com/reilandeubank/golisp/pkg/scanner"
55
)
66

7+
// environment allows for variable scope and closures. Less necessary in this implementation of Lisp
78
type environment struct {
89
enclosing *environment
9-
values map[string]interface{}
10+
values map[string]interface{}
1011
}
1112

1213
func NewEnvironment() environment {
@@ -17,10 +18,12 @@ func NewEnvironmentWithEnclosing(Enclosing environment) environment {
1718
return environment{enclosing: &Enclosing, values: make(map[string]interface{})}
1819
}
1920

21+
// define a variable name as the passed value. only allowed in global scope
2022
func (e *environment) define(name string, value interface{}) {
21-
e.values[name] = value // this allows for variable redefinition. May be weird in normal code, but is useful for REPL
23+
e.values[name] = value // this allows for variable redefinition. May be weird in normal code, but is useful for REPL
2224
}
2325

26+
// get the value of a variable name. searches in enclosing environments, but throws if the variable is never found
2427
func (e *environment) get(name scanner.Token) (interface{}, error) {
2528
value, ok := e.values[name.Lexeme]
2629
if !ok && e.enclosing != nil {
@@ -30,14 +33,3 @@ func (e *environment) get(name scanner.Token) (interface{}, error) {
3033
}
3134
return value, nil
3235
}
33-
34-
func (e *environment) assign(name scanner.Token, value interface{}) error {
35-
_, ok := e.values[name.Lexeme]
36-
if !ok && e.enclosing != nil {
37-
return e.enclosing.assign(name, value)
38-
} else if !ok {
39-
return &RuntimeError{Token: name, Message: "Undefined variable '" + name.Lexeme}
40-
}
41-
e.values[name.Lexeme] = value
42-
return nil
43-
}

pkg/interpreter/errorHandler.go

+2-10
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,8 @@ import (
66
"github.com/reilandeubank/golisp/pkg/scanner"
77
)
88

9-
var hadErrorFlag bool = false
10-
11-
func HadError() bool {
12-
return hadErrorFlag
13-
}
14-
15-
func SetErrorFlag(val bool) {
16-
hadErrorFlag = val
17-
}
18-
9+
// RuntimeError defines a new Error type. Did not know this was possible until I started work on
10+
// the interpreter, so this would be the better way to implement my scanner and parser errors
1911
type RuntimeError struct {
2012
Token scanner.Token
2113
Message string

pkg/interpreter/helpers.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import (
88
// "github.com/reilandeubank/golisp/pkg/parser"
99
)
1010

11+
// isTruthy shares a definition of truthiness with Go. 'nil', 0, and 'false' are false, all else are true
1112
func isTruthy(object interface{}) bool {
12-
if object == nil || object == 0.0 || object == 0 { // only false, nil, and 0 are falsey
13+
if object == nil || object == 0 { // only false, nil, and 0 are falsey
1314
return false
1415
}
1516

@@ -41,4 +42,4 @@ func checkNumberOperands(operator scanner.Token, left interface{}, right interfa
4142
return nil
4243
}
4344
return &RuntimeError{Token: operator, Message: "Operators must be numbers"}
44-
}
45+
}

pkg/interpreter/interpreter.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,22 @@ import (
77

88
type Interpreter struct {
99
environment *environment
10-
globals *environment
10+
globals *environment
1111
}
1212

13+
// NewInterpreter defines an interpreter instance where the environment and globals are the same environment
1314
func NewInterpreter() Interpreter {
1415
global := NewEnvironment()
1516
return Interpreter{environment: &global, globals: &global}
1617
}
1718

19+
// evaluate calls the Accept method on a single expression
1820
func (i *Interpreter) evaluate(expr parser.Expression) (interface{}, error) {
1921
return expr.Accept(i)
2022
}
2123

22-
func (i *Interpreter) Interpret(exprs []parser.Expression) (error) {
24+
// Interpret will evaluate all expressions in the source code, printing out returned values
25+
func (i *Interpreter) Interpret(exprs []parser.Expression) error {
2326
for _, expr := range exprs {
2427
out, err := i.evaluate(expr)
2528
if out != nil {
@@ -32,6 +35,8 @@ func (i *Interpreter) Interpret(exprs []parser.Expression) (error) {
3235
return nil
3336
}
3437

38+
// evaluateFunction will call evaluate the function's expression
39+
// and then return the current environment to normal after completion
3540
func (i *Interpreter) evaluateFunction(expression parser.Expression, environment environment) (interface{}, error) {
3641
previous := i.environment
3742

@@ -41,4 +46,4 @@ func (i *Interpreter) evaluateFunction(expression parser.Expression, environment
4146

4247
i.environment = &environment
4348
return i.evaluate(expression)
44-
}
49+
}

pkg/interpreter/visitExpr.go

+26-20
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import (
88
"github.com/reilandeubank/golisp/pkg/scanner"
99
)
1010

11+
// VisitListExpr evaluates a list which could be an operation, keyword and arguments, or a simple list of atoms
1112
func (i *Interpreter) VisitListExpr(l parser.ListExpr) (interface{}, error) {
1213
switch head := l.Head.(type) {
14+
// adds the tail as the operator's operands and evaluates the operator
1315
case parser.Operator:
1416
head.Operands = l.Tail
1517
result, err := i.evaluate(head)
@@ -20,9 +22,11 @@ func (i *Interpreter) VisitListExpr(l parser.ListExpr) (interface{}, error) {
2022
return nil, nil
2123
}
2224
return result, nil
25+
// adds the tail as the keyword's arguments and evaluates it
2326
case parser.Keyword:
2427
head.Args = l.Tail
2528
return i.evaluate(head)
29+
// will simply return a list of atoms
2630
case parser.Atom:
2731
returnList := parser.ListExpr{Head: head, Tail: l.Tail}
2832
return returnList, nil
@@ -31,15 +35,15 @@ func (i *Interpreter) VisitListExpr(l parser.ListExpr) (interface{}, error) {
3135
return nil, fmt.Errorf("LISTEXPR not implemented")
3236
}
3337

38+
// VisitKeywordExpr evaluates a syntax node where the keyword is of one of the
39+
// below types, and it contains a list of "arguments
3440
func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
3541
switch k.Keyword.Type {
36-
case scanner.TRUE:
42+
case scanner.TRUE: // TRUE keyword maps to Go's 'true' value
3743
return true, nil
38-
// case scanner.FALSE:
39-
// return false, nil
40-
case scanner.NIL:
44+
case scanner.NIL: // NIL keyword maps to Go's 'nil' value (is also treated like a false value)
4145
return nil, nil
42-
case scanner.CAR:
46+
case scanner.CAR: // car returns the first element of a list as an atom
4347
car, err := i.evaluate(k.Args[0])
4448
switch car.(type) {
4549
case parser.ListExpr:
@@ -50,7 +54,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
5054
return nil, err
5155
}
5256
return car, nil
53-
case scanner.CDR:
57+
case scanner.CDR: // cdr returns the entire list other than the first element
5458
output, err := i.evaluate(k.Args[0])
5559
if err != nil {
5660
return nil, err
@@ -65,7 +69,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
6569
} else {
6670
return parser.ListExpr{Head: list.Tail[0]}, nil
6771
}
68-
case scanner.COND:
72+
case scanner.COND: // cond is of the form (cond c1 r1 c2 r2...), where if c_n is true, r_n will be evaluated
6973
for j := 0; j < len(k.Args); j += 2 {
7074
condition, err := i.evaluate(k.Args[j])
7175
if err != nil {
@@ -76,7 +80,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
7680
}
7781
}
7882
return nil, &RuntimeError{Token: k.Keyword, Message: "Lack of true condition"}
79-
case scanner.NUMBERQ:
83+
case scanner.NUMBERQ: // number? returns true if the argument is of type 'number', else nil
8084
if len(k.Args) != 1 {
8185
return nil, &RuntimeError{Token: k.Keyword, Message: "NUMBER? operation must have 1 operand"}
8286
}
@@ -85,12 +89,12 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
8589
return nil, err
8690
}
8791
return checkNumberOperand(k.Keyword, expr)
88-
case scanner.SYMBOLQ:
92+
case scanner.SYMBOLQ: // symbol? returns true if the argument is a symbol, else nil
8993
if len(k.Args) != 1 {
9094
return nil, &RuntimeError{Token: k.Keyword, Message: "SYMBOL? operation must have 1 operand"}
9195
}
9296
return reflect.TypeOf(k.Args[0]) == reflect.TypeOf(parser.Symbol{}), nil
93-
case scanner.LISTQ:
97+
case scanner.LISTQ: // list? returns true if the argument is a list, else nil
9498
if len(k.Args) != 1 {
9599
return nil, &RuntimeError{Token: k.Keyword, Message: "LIST? operation must have 1 operand"}
96100
}
@@ -99,7 +103,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
99103
return nil, err
100104
}
101105
return reflect.TypeOf(expr) == reflect.TypeOf(parser.ListExpr{}), nil
102-
case scanner.NILQ:
106+
case scanner.NILQ: // nil? returns true if the argument is nil, else nil
103107
if len(k.Args) != 1 {
104108
return nil, &RuntimeError{Token: k.Keyword, Message: "NIL? operation must have 1 operand"}
105109
}
@@ -108,7 +112,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
108112
return nil, err
109113
}
110114
return expr == nil, nil
111-
case scanner.ANDQ:
115+
case scanner.ANDQ: // and? is the logical AND operation
112116
if len(k.Args) != 2 {
113117
return nil, &RuntimeError{Token: k.Keyword, Message: "AND? operation must have 2 operands"}
114118
}
@@ -121,7 +125,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
121125
return nil, err
122126
}
123127
return isTruthy(left) && isTruthy(right), nil
124-
case scanner.ORQ:
128+
case scanner.ORQ: // or? is the logical OR operation
125129
if len(k.Args) != 2 {
126130
return nil, &RuntimeError{Token: k.Keyword, Message: "OR? operation must have 2 operands"}
127131
}
@@ -138,7 +142,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
138142
return nil, nil
139143
}
140144
return result, nil
141-
case scanner.NOTQ:
145+
case scanner.NOTQ: // not? is the logical NOT operator
142146
if len(k.Args) != 2 {
143147
return nil, &RuntimeError{Token: k.Keyword, Message: "NOT? operation must have 1 operand"}
144148
}
@@ -147,7 +151,7 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
147151
return nil, err
148152
}
149153
return !isTruthy(expr), nil
150-
case scanner.SET:
154+
case scanner.SET: // SET will declare and initialize a global variable with the first operand as the name and the second operand as the value
151155
if len(k.Args) != 2 {
152156
return nil, &RuntimeError{Token: k.Keyword, Message: "SET operation must have 2 operands"}
153157
}
@@ -160,12 +164,13 @@ func (i *Interpreter) VisitKeywordExpr(k parser.Keyword) (interface{}, error) {
160164
}
161165
i.environment.define(k.Args[0].(parser.Symbol).Name.Lexeme, value)
162166
return nil, nil
167+
default:
168+
return nil, fmt.Errorf("KEYWORDEXPR not implemented")
163169
}
164-
return nil, fmt.Errorf("KEYWORDEXPR not implemented")
165170
}
166171

167172
func (i *Interpreter) VisitOperatorExpr(o parser.Operator) (interface{}, error) {
168-
if len(o.Operands) != 2 {
173+
if len(o.Operands) != 2 { // ensures that binary operators only receive 2 operands
169174
return nil, &RuntimeError{Token: o.Operator, Message: "Binary operation must only have two operands"}
170175
}
171176
left, err := i.evaluate(o.Operands[0])
@@ -184,7 +189,7 @@ func (i *Interpreter) VisitOperatorExpr(o parser.Operator) (interface{}, error)
184189
return nil, err
185190
}
186191
return left.(float64) - right.(float64), nil
187-
case scanner.PLUS:
192+
case scanner.PLUS: // + can be used with a pair of numbers or a pair of strings
188193
if reflect.TypeOf(left) == reflect.TypeOf("") && reflect.TypeOf(right) == reflect.TypeOf("") {
189194
return left.(string) + right.(string), nil
190195
}
@@ -218,8 +223,9 @@ func (i *Interpreter) VisitOperatorExpr(o parser.Operator) (interface{}, error)
218223
return left.(float64) < right.(float64), nil
219224
case scanner.EQUAL:
220225
return isEqual(left, right), nil
226+
default:
227+
return nil, &RuntimeError{Token: o.Operator, Message: "Invalid operator"}
221228
}
222-
return nil, &RuntimeError{Token: o.Operator, Message: "Invalid operator"}
223229
}
224230

225231
func (i *Interpreter) VisitAtomExpr(a parser.Atom) (interface{}, error) {
@@ -230,7 +236,7 @@ func (i *Interpreter) VisitSymbolExpr(s parser.Symbol) (interface{}, error) {
230236
return i.environment.get(s.Name)
231237
}
232238

233-
func (i *Interpreter) VisitCallExpr (c parser.Call) (interface{}, error) {
239+
func (i *Interpreter) VisitCallExpr(c parser.Call) (interface{}, error) {
234240
callee, err := i.evaluate(c.Callee)
235241
if err != nil {
236242
return nil, err

0 commit comments

Comments
 (0)