Skip to content

Commit 92ee993

Browse files
committed
Add While loop and many more
-> Add while loop with wakati keyword -> Add dictionaries and lists with + operator -> Add new item to dictionary with dict[index] = item -> acha is now only necessary when initializing a variable -> Better error messages -> Multiple bug fixes
1 parent 11b631d commit 92ee993

File tree

10 files changed

+285
-49
lines changed

10 files changed

+285
-49
lines changed

ast/ast.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (p *Program) String() string {
4545
}
4646

4747
type LetStatement struct {
48-
Token token.Token // only placed to print errors
48+
Token token.Token
4949
Name *Identifier
5050
Value Expression
5151
}
@@ -96,7 +96,7 @@ func (rs *ReturnStatement) String() string {
9696
}
9797

9898
type ExpressionStatement struct {
99-
Token token.Token // the first token of the expression
99+
Token token.Token
100100
Expression Expression
101101
}
102102

@@ -193,7 +193,7 @@ func (ie *IfExpression) String() string {
193193
}
194194

195195
type BlockStatement struct {
196-
Token token.Token // the {
196+
Token token.Token
197197
Statements []Statement
198198
}
199199

@@ -330,3 +330,40 @@ func (dl *DictLiteral) String() string {
330330

331331
return out.String()
332332
}
333+
334+
type AssignmentExpression struct {
335+
Token token.Token
336+
Left Expression
337+
Value Expression
338+
}
339+
340+
func (ae *AssignmentExpression) expressionNode() {}
341+
func (ae *AssignmentExpression) TokenLiteral() string { return ae.Token.Literal }
342+
func (ae *AssignmentExpression) String() string {
343+
var out bytes.Buffer
344+
345+
out.WriteString(ae.Left.String())
346+
out.WriteString(ae.TokenLiteral())
347+
out.WriteString(ae.Value.String())
348+
349+
return out.String()
350+
}
351+
352+
type WhileExpression struct {
353+
Token token.Token
354+
Condition Expression
355+
Consequence *BlockStatement
356+
}
357+
358+
func (we *WhileExpression) expressionNode() {}
359+
func (we *WhileExpression) TokenLiteral() string { return we.Token.Literal }
360+
func (we *WhileExpression) String() string {
361+
var out bytes.Buffer
362+
363+
out.WriteString("while")
364+
out.WriteString(we.Condition.String())
365+
out.WriteString(" ")
366+
out.WriteString(we.Consequence.String())
367+
368+
return out.String()
369+
}

evaluator/builtins.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
var builtins = map[string]*object.Builtin{
13-
"idadi": &object.Builtin{
13+
"idadi": {
1414
Fn: func(args ...object.Object) object.Object {
1515
if len(args) != 1 {
1616
return newError("Hoja hazilingani, tunahitaji=1, tumepewa=%d", len(args))
@@ -26,7 +26,7 @@ var builtins = map[string]*object.Builtin{
2626
}
2727
},
2828
},
29-
"yamwisho": &object.Builtin{
29+
"yamwisho": {
3030
Fn: func(args ...object.Object) object.Object {
3131
if len(args) != 1 {
3232
return newError("Samahani, tunahitaji Hoja moja tu, wewe umeweka %d", len(args))
@@ -44,7 +44,7 @@ var builtins = map[string]*object.Builtin{
4444
return NULL
4545
},
4646
},
47-
"sukuma": &object.Builtin{
47+
"sukuma": {
4848
Fn: func(args ...object.Object) object.Object {
4949
if len(args) != 2 {
5050
return newError("Samahani, tunahitaji Hoja 2, wewe umeweka %d", len(args))
@@ -56,30 +56,26 @@ var builtins = map[string]*object.Builtin{
5656
arr := args[0].(*object.Array)
5757
length := len(arr.Elements)
5858

59-
newElements := make([]object.Object, length+1, length+1)
59+
newElements := make([]object.Object, length+1)
6060
copy(newElements, arr.Elements)
6161
newElements[length] = args[1]
6262

6363
return &object.Array{Elements: newElements}
6464
},
6565
},
66-
"jaza": &object.Builtin{
66+
"jaza": {
6767
Fn: func(args ...object.Object) object.Object {
6868

69-
if len(args) > 1 || len(args) < 0 {
69+
if len(args) > 1 {
7070
return newError("Samahani, hii function inapokea hoja 0 au 1, wewe umeweka %d", len(args))
7171
}
7272

7373
if len(args) > 0 && args[0].Type() != object.STRING_OBJ {
7474
return newError(fmt.Sprintf(`Tafadhali tumia alama ya nukuu: "%s"`, args[0].Inspect()))
7575
}
76-
//if err := object.Check(
77-
// "jaza", args, object.RangeOfArgs(0, 1), object.WithTypes(object.STRING_OBJ)); err != nil {
78-
// return newError("Kuna kitu umebolonga aisee")
79-
//}
8076
if len(args) == 1 {
8177
prompt := args[0].(*object.String).Value
82-
fmt.Fprintf(os.Stdout, prompt)
78+
fmt.Fprint(os.Stdout, prompt)
8379
}
8480

8581
buffer := bufio.NewReader(os.Stdin)
@@ -92,7 +88,7 @@ var builtins = map[string]*object.Builtin{
9288
return &object.String{Value: string(line)}
9389
},
9490
},
95-
"chapa": &object.Builtin{
91+
"chapa": {
9692
Fn: func(args ...object.Object) object.Object {
9793
if len(args) == 0 {
9894
fmt.Println("")

evaluator/evaluator.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package evaluator
22

33
import (
44
"fmt"
5+
"strings"
56

67
"github.com/AvicennaJr/Nuru/ast"
78
"github.com/AvicennaJr/Nuru/object"
@@ -105,6 +106,57 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
105106
return evalIndexExpression(left, index)
106107
case *ast.DictLiteral:
107108
return evalDictLiteral(node, env)
109+
case *ast.WhileExpression:
110+
return evalWhileExpression(node, env)
111+
case *ast.AssignmentExpression:
112+
left := Eval(node.Left, env)
113+
if isError(left) {
114+
return left
115+
}
116+
117+
value := Eval(node.Value, env)
118+
if isError(value) {
119+
return value
120+
}
121+
122+
if ident, ok := node.Left.(*ast.Identifier); ok {
123+
env.Set(ident.Value, value)
124+
} else if ie, ok := node.Left.(*ast.IndexExpression); ok {
125+
obj := Eval(ie.Left, env)
126+
if isError(obj) {
127+
return obj
128+
}
129+
130+
if array, ok := obj.(*object.Array); ok {
131+
index := Eval(ie.Index, env)
132+
if isError(index) {
133+
return index
134+
}
135+
if idx, ok := index.(*object.Integer); ok {
136+
if int(idx.Value) > len(array.Elements) {
137+
return newError("Index imezidi idadi ya elements")
138+
}
139+
array.Elements[idx.Value] = value
140+
} else {
141+
return newError("Hauwezi kufanya opereshen hii na %#v", index)
142+
}
143+
} else if hash, ok := obj.(*object.Dict); ok {
144+
key := Eval(ie.Index, env)
145+
if isError(key) {
146+
return key
147+
}
148+
if hashKey, ok := key.(object.Hashable); ok {
149+
hashed := hashKey.HashKey()
150+
hash.Pairs[hashed] = object.DictPair{Key: key, Value: value}
151+
} else {
152+
return newError("Hauwezi kufanya opereshen hii na %T", key)
153+
}
154+
} else {
155+
return newError("%T haifanyi operation hii", obj)
156+
}
157+
} else {
158+
return newError("Tumia neno kama variable, sio %T", left)
159+
}
108160

109161
}
110162

@@ -173,17 +225,70 @@ func evalInfixExpression(
173225
left, right object.Object,
174226
) object.Object {
175227
switch {
228+
229+
case operator == "+" && left.Type() == object.DICT_OBJ && right.Type() == object.DICT_OBJ:
230+
leftVal := left.(*object.Dict).Pairs
231+
rightVal := right.(*object.Dict).Pairs
232+
pairs := make(map[object.HashKey]object.DictPair)
233+
for k, v := range leftVal {
234+
pairs[k] = v
235+
}
236+
for k, v := range rightVal {
237+
pairs[k] = v
238+
}
239+
return &object.Dict{Pairs: pairs}
240+
241+
case operator == "+" && left.Type() == object.ARRAY_OBJ && right.Type() == object.ARRAY_OBJ:
242+
leftVal := left.(*object.Array).Elements
243+
rightVal := right.(*object.Array).Elements
244+
elements := make([]object.Object, len(leftVal)+len(rightVal))
245+
elements = append(leftVal, rightVal...)
246+
return &object.Array{Elements: elements}
247+
248+
case operator == "*" && left.Type() == object.ARRAY_OBJ && right.Type() == object.INTEGER_OBJ:
249+
leftVal := left.(*object.Array).Elements
250+
rightVal := int(right.(*object.Integer).Value)
251+
elements := leftVal
252+
for i := rightVal; i > 1; i-- {
253+
elements = append(elements, leftVal...)
254+
}
255+
return &object.Array{Elements: elements}
256+
257+
case operator == "*" && left.Type() == object.INTEGER_OBJ && right.Type() == object.ARRAY_OBJ:
258+
leftVal := int(left.(*object.Integer).Value)
259+
rightVal := right.(*object.Array).Elements
260+
elements := rightVal
261+
for i := leftVal; i > 1; i-- {
262+
elements = append(elements, rightVal...)
263+
}
264+
return &object.Array{Elements: elements}
265+
266+
case operator == "*" && left.Type() == object.STRING_OBJ && right.Type() == object.INTEGER_OBJ:
267+
leftVal := left.(*object.String).Value
268+
rightVal := right.(*object.Integer).Value
269+
return &object.String{Value: strings.Repeat(leftVal, int(rightVal))}
270+
271+
case operator == "*" && left.Type() == object.INTEGER_OBJ && right.Type() == object.STRING_OBJ:
272+
leftVal := left.(*object.Integer).Value
273+
rightVal := right.(*object.String).Value
274+
return &object.String{Value: strings.Repeat(rightVal, int(leftVal))}
275+
176276
case left.Type() == object.INTEGER_OBJ && right.Type() == object.INTEGER_OBJ:
177277
return evalIntegerInfixExpression(operator, left, right)
278+
178279
case operator == "==":
179280
return nativeBoolToBooleanObject(left == right)
281+
180282
case operator == "!=":
181283
return nativeBoolToBooleanObject(left != right)
284+
182285
case left.Type() != right.Type():
183286
return newError("Aina Hazilingani: %s %s %s",
184287
left.Type(), operator, right.Type())
288+
185289
case left.Type() == object.STRING_OBJ && right.Type() == object.STRING_OBJ:
186290
return evalStringInfixExpression(operator, left, right)
291+
187292
default:
188293
return newError("Operesheni Haielweki: %s %s %s",
189294
left.Type(), operator, right.Type())
@@ -353,6 +458,8 @@ func evalIndexExpression(left, index object.Object) object.Object {
353458
switch {
354459
case left.Type() == object.ARRAY_OBJ && index.Type() == object.INTEGER_OBJ:
355460
return evalArrayIndexExpression(left, index)
461+
case left.Type() == object.ARRAY_OBJ && index.Type() != object.INTEGER_OBJ:
462+
return newError("Tafadhali tumia number, sio: %s", index.Type())
356463
case left.Type() == object.DICT_OBJ:
357464
return evalDictIndexExpression(left, index)
358465
default:
@@ -413,3 +520,25 @@ func evalDictIndexExpression(dict, index object.Object) object.Object {
413520

414521
return pair.Value
415522
}
523+
524+
func evalWhileExpression(we *ast.WhileExpression, env *object.Environment) object.Object {
525+
var result object.Object
526+
527+
for {
528+
condition := Eval(we.Condition, env)
529+
if isError(condition) {
530+
return condition
531+
}
532+
533+
if isTruthy(condition) {
534+
result = Eval(we.Consequence, env)
535+
} else {
536+
break
537+
}
538+
}
539+
540+
if result != nil {
541+
return result
542+
}
543+
return nil
544+
}

evaluator/evaluator_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,11 @@ func TestErrorHandling(t *testing.T) {
174174
}{
175175
{
176176
"5 + kweli",
177-
"Aina Hazilingani: NAMBARI + BOOLEAN",
177+
"Aina Hazilingani: NAMBA + BOOLEAN",
178178
},
179179
{
180180
"5 + kweli; 5;",
181-
"Aina Hazilingani: NAMBARI + BOOLEAN",
181+
"Aina Hazilingani: NAMBA + BOOLEAN",
182182
},
183183
{
184184
"-kweli",
@@ -344,7 +344,7 @@ func TestBuiltinFunctions(t *testing.T) {
344344
{`idadi("")`, 0},
345345
{`idadi("four")`, 4},
346346
{`idadi("hello world")`, 11},
347-
{`idadi(1)`, "Samahani, hii function haitumiki na NAMBARI"},
347+
{`idadi(1)`, "Samahani, hii function haitumiki na NAMBA"},
348348
{`idadi("one", "two")`, "Hoja hazilingani, tunahitaji=1, tumepewa=2"},
349349
}
350350

examples/example.nr

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ chapa(list[2]);
3838
chapa(list[-100]); // prints null
3939
chapa(idadi(list));
4040
chapa(yamwisho(list));
41+
chapa([1,2,3] + [4,5,6]);
42+
list[0] = 1000
43+
chapa(list[0])
4144

4245
// if statements
4346
chapa("Testing if statements...");
@@ -88,3 +91,18 @@ chapa("Testing dictionaries...")
8891
acha watu = [{"jina": "Mojo", "kabila": "Mnyakusa"}, {"jina": "Avi", "kabila": "Mwarabu wa dubai"}]
8992

9093
chapa(watu, watu[0], watu[0]["jina"], watu[0]["kabila"])
94+
95+
watu[0]["jina"] = "MJ";
96+
chapa(watu[0]["jina"]);
97+
98+
chapa({"a":1} + {"b": 2})
99+
// testing while loop
100+
101+
chapa("Testing while loop...");
102+
103+
acha i = 10;
104+
105+
wakati (i > 0) {
106+
chapa(i);
107+
i = i - 1;
108+
}

object/object.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ import (
1212
type ObjectType string
1313

1414
const (
15-
INTEGER_OBJ = "NAMBARI"
15+
INTEGER_OBJ = "NAMBA"
1616
BOOLEAN_OBJ = "BOOLEAN"
17-
NULL_OBJ = "NULL" // Not sure if the language should have null lol
18-
RETURN_VALUE_OBJ = "RETURN_VALUE"
19-
ERROR_OBJ = "ERROR"
17+
NULL_OBJ = "NULL"
18+
RETURN_VALUE_OBJ = "RUDISHA"
19+
ERROR_OBJ = "KOSA"
2020
FUNCTION_OBJ = "FUNCTION"
2121
STRING_OBJ = "NENO"
22-
BUILTIN_OBJ = "BUILTIN"
22+
BUILTIN_OBJ = "YA_NDANI"
2323
ARRAY_OBJ = "ARRAY"
24-
DICT_OBJ = "DICT"
24+
DICT_OBJ = "KAMUSI"
2525
)
2626

2727
type Object interface {

0 commit comments

Comments
 (0)