From e86651dd3d500d1abec6bd16deac0d3ba544c6a2 Mon Sep 17 00:00:00 2001 From: akchekur Date: Wed, 1 Jun 2022 17:46:22 -0700 Subject: [PATCH] Adding implementation supporting lists in for-loops --- design/forloops-design.md | 605 ++++++++++++++++++++++++++------------ lower.ts | 103 ++++++- tests/forloops.test.ts | 149 +++++++++- type-check.ts | 52 +++- 4 files changed, 689 insertions(+), 220 deletions(-) diff --git a/design/forloops-design.md b/design/forloops-design.md index b7ef22c2..a7feccd8 100644 --- a/design/forloops-design.md +++ b/design/forloops-design.md @@ -1,27 +1,34 @@ We have implemented bsic for-loop structure that expects object as an values in[for i in values] with a conditation that the class of the object should have next, hasnext and reset methods with return type of iterator, boolean and none respectively. -Week -7 +**Week -7** + In the earlier- submitted milestones, we mentioned to work on builtin range class, but post the discussion in the class we implented above functionality. Test cases 1- 11 were submitted as initial commit for the feature. -Week -8 +**Week -8** + Test cases to cover the functionality of break, continue in while, for loops are included (single and nested loops). Also, test cases for iterator in for-loop to be a non-integer (boolean) are included. Test cases 1-29 are working post week-8. -Design Decisions: +**Week - 9/10:** + For-loops functionality is extended to support lists (variable (items), direct [1,2,3,4], function call). Test cases 31-41 are added as part of this extension. + +**Design Decisions:** 1. As per the comments on ealier pull-request, iterator type is changed to string and using the enviroment type of the iterator is computed. The function to find out the type in type-check.ts and iterator in ast.ts will be extended as when new features are included like tuples, etc. 2. Every class, which is used as values in the for loop should have reset method whose objective is to reset the current pointer to minimum. This method is called before the loop iteration to ensure the usage of object in multiple parallel loops and nested loops (test cases are presented below). 3. start, body, end labels of while and for loops are appened to the labels of enviroment to use them later for break and continue statements. 4. Test case 12 is tested in the browser for parser error, as we are not sure if modifications to asserts.test.ts are allowed, hence commented the same test case in forloops.test.ts file as well. +5. For lists, the iterator has to be of same type as list items. +6. In case of lists, the value is copied to another variable (generated from backend) and a index variable is also created at the backend. As the loop progresses, index variable is incremented by 1 and the iterator say i is assigned to list[index]. List is copied to ensure, the changes to the values within the loop, wont affect the number of times a loop is run. Below are the test cases team tried for the above-mentioned functionality. -Test case 1: - -Input: +# Test case 1: +## Input: +``` class Range(object): current : int = 0 min : int = 0 @@ -48,13 +55,15 @@ i:int = 0 cls = Range().new(1,3) for i in cls: print(i) +``` +## Output: +Error: TypeCheck failed (has no method called 'next1') -Output: Error: TypeCheck failed (has no method called 'next1') +# Test case 2: -Test case 2: - -Input: +## Input: +``` class Range(object): current : int = 0 min : int = 0 @@ -81,13 +90,14 @@ i:int = 0 cls = Range().new(1,3) for i in cls: print(i) +``` +## Output: +Error: TypeCheck failed (has no method called 'hasnext') -Output: Error: TypeCheck failed (has no method called 'hasnext') - -Test case 3: - -Input: +# Test case 3: +## Input: +``` class Range(object): current : int = 0 min : int = 0 @@ -114,12 +124,15 @@ class Range(object): cls = Range().new(1,3) for i in cls: print(i) +``` +## Output: +Error: TypeCheck failed (has no method called 'reset') - Output: Error: TypeCheck failed (has no method called 'reset') +# Test case 4: -Test case 4: +## Input: -Input: +``` class Range(object): current : int = 0 min : int = 0 @@ -146,10 +159,11 @@ i:bool = True cls = Range().new(1,3) for i in cls: print(i) +``` +## Output: Error: +TypeCheck failed -Output: Error: TypeCheck failed - - +``` Base Range Class definition: class Range(object): @@ -170,37 +184,39 @@ class Range(object): return self.current < self.max def reset(self:Range) : self.current = self.min +``` +# Test case 5: -Test case 5: - -Input: - +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(10, 1) for i in cls: print(i) +``` +## Output: +[""] -Output: [""] - -Test case 6: - -Input: +# Test case 6: +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(3, 6) for i in cls: print(i) +``` +## Output: +[3, 4, 5] -Output:[3, 4, 5] - -Test case 7: - -Input: +# Test case 7: +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(1, 4) @@ -209,13 +225,14 @@ for i in cls: print(i) i = 10 print(i) +``` +## Output: +1, 10, 2, 10, 3, 10 -Output: 1, 10, 2, 10, 3, 10 - -Test case 8: - -Input: +# Test case 8: +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(1, 4) @@ -224,13 +241,14 @@ for i in cls: print(i) i = True print(i) +``` +## Output: +Error: TypeCheck error (iterator type changing inside loop) -Output: Error: TypeCheck error (iterator type changing inside loop) - -Test case 9: - -Input: +# Test case 9: +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(1, 4) @@ -242,13 +260,14 @@ for i in cls: i = 20 print(i) +``` +## Output: +1, 2, 3, 20 -Output: 1, 2, 3, 20 - -Test case 10: - -Input: +# Test case 10: +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(1, 4) @@ -260,13 +279,14 @@ for i in cls: i = 20 print(i) +``` +## Output: +1, 20 -Output:1, 20 - -Test case 11: - -Input: +# Test case 11: +## Input: +``` def checkForLoop(n:int): c:Range = None i:int = 0 @@ -276,25 +296,27 @@ def checkForLoop(n:int): return checkForLoop(2) +``` +## Output: +2, 3, 4, 5, 6 -Output:2, 3, 4, 5, 6 - -Test case 12: - -Input: +# Test case 12: +## Input: +``` cls:Range = None i:int = 0 cls = Range().new(1,3) for 1+2 in cls: print(i) +``` +## Output: Error: +parser error -Output: Error: parser error - -Test case 13: - -Input: +# Test case 13: +## Input: +``` i:int = 0 cls: Range = None cls = Range().new(1,3) @@ -304,13 +326,14 @@ for i in cls: for i in cls: print(i) +``` +## Output: +1,2,1,2 -Output: 1,2,1,2 - -Test case 14: - -Input: +# Test case 14: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -322,13 +345,14 @@ for i in cls: print(i) for j in innercls: print(j) +``` +## Output: +1, 5, 6, 2, 5, 6, 3, 5 -Output: 1, 5, 6, 2, 5, 6, 3, 5 - -Test case 15: - -Input: +# Test case 15: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -342,13 +366,14 @@ for i in cls: print(j) continue print(i+1) +``` +## Output: +1, 5, 6, 2, 5, 6, -Output: 1, 5, 6, 2, 5, 6, - -Test case 16: - -Input: +# Test case 16: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -362,13 +387,14 @@ for i in cls: print(j) break print(i+1) +``` +## Output: + 1, 5, 2, 5 -Output: 1, 5, 2, 5 - -Test case 17: - -Input: +# Test case 17: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -381,13 +407,14 @@ for i in cls: break for j in innercls: print(j) +``` +## Output: +1 -Output: 1 - -Test case 18: - -Input: +# Test case 18: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -400,13 +427,14 @@ for i in cls: continue for j in innercls: print(j) +``` +## Output: +1, 2 -Output: 1, 2 - -Test case 19: - -Input: +# Test case 19: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -419,13 +447,14 @@ for i in cls: break for j in innercls: print(j) +``` +## Output: +1 -Output: 1 - -Test case 20: - -Input: +# Test case 20: +## Input: +``` cls:Range = None innercls:Range = None i:int = 0 @@ -438,13 +467,14 @@ for i in cls: for j in innercls: print(j-1) print(i+1) +``` +## Output: +1, 4, 5, 2, 2, 4, 5, 3 -Output: 1, 4, 5, 2, 2, 4, 5, 3 - -Test case 21: - -Input: +# Test case 21: +## Input: +``` i:int = 0 while(i<10): @@ -452,13 +482,14 @@ while(i<10): if(i==5): continue print(i) +``` +## Output: +1, 2, 3, 4, 6, 7, 8, 9, 10 -Output: 1, 2, 3, 4, 6, 7, 8, 9, 10 - -Test case 22: - -Input: +# Test case 22: +## Input: +``` i:int = 0 j:int = 5 while(i<3): @@ -471,12 +502,14 @@ while(i<3): print(j) j = 5 +``` +## Output: +1, 3, 2, 1, 0, 2, 3, 2, 1, 0, 3, 3, 2, 1, 0 -Output: 1, 3, 2, 1, 0, 2, 3, 2, 1, 0, 3, 3, 2, 1, 0 +# Test case 23: -Test case 23: - -Input: +## Input: +``` i:int = 0 j:int = 5 while(i<3): @@ -488,13 +521,14 @@ while(i<3): j = j-1 print(j) j = 5 +``` +## Output: +2, 4, 3, 2, 1, 0, 3, 4, 3, 2, 1, 0 -Output: 2, 4, 3, 2, 1, 0, 3, 4, 3, 2, 1, 0 - -Test case 24: - -Input: +# Test case 24: +## Input: +``` i:int = 0 j:int = 5 while(i<3): @@ -506,12 +540,14 @@ while(i<3): j = j-1 print(j) j = 5 +``` +## Output: +1, 4, 3, 2, 1, 0, 2 -Output: 1, 4, 3, 2, 1, 0, 2 - -Test case 25: +# Test case 25: -Input: +## Input: +``` i:int = 0 j:int = 5 while(i<3): @@ -523,12 +559,14 @@ while(i<3): break print(j) j = 5 +``` +## Output: +1, 2, 3 -Output: 1, 2, 3 +# Test case 26: -Test case 26: - -Input: +## Input: +``` def breakInFunction(n:int, b:int)->int: i:int = 0 sum:int = 0 @@ -540,13 +578,14 @@ def breakInFunction(n:int, b:int)->int: return sum print(breakInFunction(10,5)) +``` +## Output: +10 -Output: 10 - -Test case 27: - -Input: +# Test case 27: +## Input: +``` def breakInFunction(n:int, b:int)->int: i:int = 0 sum:int = 0 @@ -558,26 +597,29 @@ def breakInFunction(n:int, b:int)->int: return sum print(breakInFunction(10,5)) +``` +## Output: +50 -Output: 50 - -Test case 28: - -Input: +# Test case 28: +## Input: +``` i:bool = True cls:Range = None cls = Range().new(1, 5) for i in cls: print(i) - +``` -Output: False, True, False, True +## Output: +False, True, False, True -Test case 29: +# Test case 29: -Input: +## Input: +``` class BoolRange(object): current : int = 0 @@ -616,85 +658,272 @@ def checkMultiLoopFunction(min:int, max:int) ->int: print(checkMultiLoopFunction(1,6)) +``` -Output: False, True, 1, 2, 3, 4, 5, False, True, 1, 2, 3, 4, 5, False, 30 +## Output: +False, True, 1, 2, 3, 4, 5, False, True, 1, 2, 3, 4, 5, False, 30 -Week-9 Plan: +# **Week-9 Plan:** The plan for week 9 is to extend the functionality of for-loops to iterate over lists, sets, dictionaries, strings with support of tuples as iterator along with fixing any merge conflicts with other functionalities. Below are the test cases for verifying above-mentioned functionalities. -Test case 1: strings +# Test case 1: strings + ``` str:string = "test" for i in str: print(i) + ``` - Output: t - e - s - t - -Test case 2: empty strings + ## Output: + t + e + s + t +# Test case 2: empty strings + ``` str:string = "" for i in str: print(i) - Output: "" + ``` +## Output: +"" (Empty string) -Test case 3: lists +# Test case 3: lists + ``` i:int = 0 items: [int] = None items = [0, 1, 2, 5] for i in items: print(i) + ``` - Output: 0, 1, 2, 5 +## Output: + 0, 1, 2, 5 -Test case 4: lists-sub array indexing - Test case: +# Test case 4: lists-sub array indexing + ``` items: [int] = None items = [0, 1, 2, 5] for i in items[0:3]: print(i) - - Output: 0, 1, 2, 5 - -Test case 5: sets - s:set = set() - s = {1,2,5,7} - for i in s: - print(i) - Output : 1, 2, 5, 7 - -Test case 6: BigNums - - i:int = 0 - for i in Range(1000000000000000, 1000000000000002): - print(i) - Output : 1000000000000000, 1000000000000001 - -Test case 7: set update - s:set = set() - s = {1,2,5,7} - for i in s: - print(i) - s.add(8) - Output : 1, 2, 5, 7 + ``` + + ## Output: + 0, 1, 2, 5 +# Test case 5: sets + ``` + s:set = set() + s = {1,2,5,7} + for i in s: + print(i) + ``` +## Output : + 1, 2, 5, 7 + +# Test case 6: BigNums + ``` + i:int = 0 + for i in Range(1000000000000000, 1000000000000002): + print(i) + ``` + ## Output : + 1000000000000000, 1000000000000001 + +# Test case 7: set update + ``` + s:set = set() + s = {1,2,5,7} + for i in s: + print(i) + s.add(8) +``` +## Output : 1, 2, 5, 7 Python implementation for the same gives output as 1, 2, 5, 7 which can be obtained by taking a copy of values to iterate over which ensures the numebr of times a loop is run. -Test case 8: list update +# Test case 8: list update +``` + i:int = 0 + items: [int] = None + items = [0, 1, 2, 5] + for i in items: + print(i) + items = [1, 3, 5] +``` +## Output: + 0, 1, 2, 5 + + Python implementation for the same gives output as 0, 1, 2, 5 which can be obtained by taking a copy of values to iterate over which ensures the numebr of times a loop is run. - i:int = 0 - items: [int] = None - items = [0, 1, 2, 5] - for i in items: - print(i) - items = [1, 3, 5] +# **Week-9/10 Update:** + +Below are the testcases whose implementation is supported in week 9/10. Due to the merge delays, extension of for-loops to strings/sets etc was not done. Extension to include lists is implemented. + +# Test case 1 (31): List stored in a variable + +## Input: +``` +items:[int] = None +index:int = 0 +i:int = 0 + +items = [1,2,5,60] +for i in items: + print(i) +``` +## Output: +1, 2, 5, 60 + +# Test case 2 (32): Boolean List stored in a variable + +## Input: +``` +items:[bool] = None +index:int = 0 +i:bool = True + +items = [True, False, True, False] +for i in items: + print(i) +``` +## Output: +True, False, True, False + +# Test case 3 (33): Empty List + +## Input: + +``` +items:[bool] = None +index:int = 0 +i:bool = True +for i in items: + print(i) + +print(not(i)) +``` + +## Output: +False - Output: 0, 1, 2, 5 +# Test case 4 (34): If condiiton - lists + +## Input: +``` +items:[int] = None +index:int = 0 +i:int = 0 +items = [1,2,3,4] +for i in items: + if(i%2==0): + print(i) +``` +## Output: +2, 4 + +# Test case 5 (35): Continue - Lists + +## Input: +``` +items:[int] = None +index:int = 0 +i:int = 0 +items = [1,2,3,4] +for i in items: + if(i%2==0): + continue + print(i) +``` +## Output: +1, 3 + +# Test case 6 (36): Break - Lists + +## Input: +``` +items:[int] = None +index:int = 0 +i:int = 0 +items = [1,2,3,4] +for i in items: + print(i) + if(i%2==0): + break +``` +## Output: +1, 2 + +# Test case 7 (37): Direct Lists + +## Input: +``` +index:int = 0 +i:int = 0 +for i in [10,20,30,40]: + print(i) +``` +## Output: +10,20,30,40 + +# Test case 8 (38): Function - Lists + +## Input: +``` +def returnList(n:int)->[int]: + return [n, n+1, n+2,n+3,n+4] + +i:int = 0 +for i in returnList(9): + print(i) +``` +## Output: +9, 10, 11, 12, 13 + + +# Test case 9 (39): Loop-list-within-function + +## Input: +``` +def loopList(): + i:int = 0 + for i in [2,3,4,5]: + print(i) + +loopList() +``` +## Output: +2, 3, 4, 5 + +# Test case 10 (40): Changing the list + +## Input: +``` +i:int = 0 +items:[int] = None +items = [1,2,3,5] +for i in items: + print(i) + items = [2,3] +``` +## Output: +1, 2, 3, 5 + +# Test case 11 (41): Basic Nested Lists + +## Input: +``` +a :[[int]] = None +i:[int] = None +j:int = 0 +a = [[1,2,3,4], [10,20]] +for j in a[0]: + print(j) +``` +## Output: +1, 2, 3, 4 - Python implementation for the same gives output as 0, 1, 2, 5 which can be obtained by taking a copy of values to iterate over which ensures the numebr of times a loop is run. \ No newline at end of file diff --git a/lower.ts b/lower.ts index 75d4528c..6daafd33 100644 --- a/lower.ts +++ b/lower.ts @@ -565,37 +565,34 @@ function flattenStmt(s : AST.Stmt, blocks: Array = {tag:"method-call", obj:s.values, method:"reset", arguments:[], a:{...s.a, type: NONE}}; - var resetStmt : AST.Stmt[] = [{ tag: "expr", expr: resetCall , a:{ ...s.a, type: NONE }}]; - flattenStmts(resetStmt, blocks, localenv); + var [preprocessinits, preprocessclasses] = preProcessForLoop(s, blocks, localenv, forListName, forIndexLbl); + pushStmtsToLastBlock(blocks, {tag:"jmp", lbl: forStartLbl }) blocks.push({ a: s.a, label: forStartLbl, stmts: [] }) - var hasnextCall : AST.Expr = {tag:"method-call", obj:s.values, method:"hasnext", arguments:[], a:{...s.a, type: BOOL}} - var nextCall : AST.Expr = {tag:"method-call", obj:s.values, method:"next", arguments:[], a: s.a} + var [hasnextCall, nextAssign] = generateNextandHasNextCallsForLoops(s, blocks, localenv, forIndexLbl) var [cinits, cstmts, cexpr] = flattenExprToVal(hasnextCall, blocks, localenv); pushStmtsToLastBlock(blocks, ...cstmts, { tag: "ifjmp", cond: cexpr, thn: forbodyLbl, els: forEndLbl }); - + blocks.push({ a: s.a, label: forbodyLbl, stmts: [] }) - var assignable : AST.Assignable = { tag: "id", name: s.iterator }; - var assignVar : AST.AssignVar = { target: assignable, ignorable: false, star: false }; - var destructureAss : AST.DestructuringAssignment = { isSimple: true, vars: [assignVar] }; - var nextAssign : AST.Stmt[] = [{tag:"assign", destruct: destructureAss, value: nextCall,a:s.a }] - - flattenStmts(nextAssign, blocks, localenv); // to add wasm code for i = c.next(). has no inits + // to add wasm code for i = c.next(). has no inits. List stmts i = items[index] have + var [nextinits, nextclasses] = flattenStmts(nextAssign, blocks, localenv); + var [bodyinits, bodyclasses] = flattenStmts(s.body, blocks, localenv) + pushStmtsToLastBlock(blocks, { tag: "jmp", lbl: forStartLbl }); blocks.push({ a: s.a, label: forEndLbl, stmts: [] }) - return [[...cinits, ...bodyinits], [...bodyclasses]]; + return [[...preprocessinits,...cinits, ...nextinits, ...bodyinits], [...preprocessclasses,...nextclasses,...bodyclasses]]; } } @@ -978,4 +975,80 @@ function pushStmtsToLastBlock(blocks: Array>, ...stmts export function flattenWasmInt(val: number): IR.Value{ return { tag: "wasmint", value: val } -} \ No newline at end of file +} + +/// function to modify the loop values(iterable class/lists) or the iterator before the start of the loop +export function preProcessForLoop(s: AST.Stmt, blocks: Array>, localenv: GlobalEnv, forListName:string, forIndexLbl:string): [Array>, Array>] { + if(s.tag!=="for") + return [[],[]] + switch(s.values.a.type.tag){ + case "class": + // reset the values class to the original state at the start of the loop - nested loops use case + var resetCall : AST.Expr = {tag:"method-call", obj:s.values, method:"reset", arguments:[], a:{...s.a, type: NONE}}; + var resetStmt : AST.Stmt[] = [{ tag: "expr", expr: resetCall , a:{ ...s.a, type: NONE }}]; + flattenStmts(resetStmt, blocks, localenv); + break + case "list": + // converting construct-list [1,2,3,4] to forlistname : List = None , forlistname = [1,2,3,4] and changing values to forlistname + if(["call","construct-list","index","id"].includes(s.values.tag)) + { + var varDefForListandIndex : IR.VarInit[] = [ + { a: {type:NUM}, name: forIndexLbl, type: NUM, value: {tag:"num", value:0n} }, + { a: s.values.a, name: forListName, type: s.values.a.type, value: { a: { type: { tag: "none"} }, tag: "none" } } + ]; + var listAssignable : AST.Assignable = { tag: "id", name: forListName }; + var listassignVar : AST.AssignVar = { target: listAssignable, ignorable: false, star: false }; + var listdestructureAss : AST.DestructuringAssignment = { isSimple: true, vars: [listassignVar] }; + var updateList : AST.Stmt[] = [{tag:"assign", destruct: listdestructureAss, value: s.values , a:{type:NONE} }] + var [updateinits, updateclasses] = flattenStmts(updateList, blocks, localenv) + s.values = { tag: "id", name: forListName, a:s.values.a } + return [[...varDefForListandIndex, ...updateinits],[...updateclasses]] + } + break; + } + return [[],[]] +} +/// function to generate code for next and has next equivalent stmts in case of iterable class/list +export function generateNextandHasNextCallsForLoops(s: AST.Stmt, blocks: Array>, localenv: GlobalEnv, forIndexName:string):[AST.Expr, AST.Stmt[]]{ + if(s.tag!=="for") + return + var hasnextCall : AST.Expr , nextAssign : AST.Stmt[] + + var assignable : AST.Assignable = { tag: "id", name: s.iterator }; + var assignVar : AST.AssignVar = { target: assignable, ignorable: false, star: false }; + var destructureAss : AST.DestructuringAssignment = { isSimple: true, vars: [assignVar] }; + + switch(s.values.a.type.tag){ + case "class": + hasnextCall = {tag:"method-call", obj:s.values, method:"hasnext", arguments:[], a:{...s.a, type: BOOL}} + var nextCall : AST.Expr = {tag:"method-call", obj:s.values, method:"next", arguments:[], a: s.a} + nextAssign = [{tag:"assign", destruct: destructureAss, value: nextCall,a:s.a }] + break + case "list": + if(s.values.tag === "id") + { + // if(index = {tag:"index", obj:s.values, index: {tag:"id", name:forIndexName, a:{type:NUM}}, a: {type:NONE}} + + nextAssign = [{tag:"assign", destruct: destructureAss, value: indexCall, a:s.a }] + assignable = { tag: "id", name: forIndexName }; + assignVar = { target: assignable, ignorable: false, star: false }; + destructureAss = { isSimple: true, vars: [assignVar] }; + + //index = index+1 + var incrementIndexCall : AST.Expr = {tag:"binop", a:{type:NUM}, op: 0, + left:{tag:"id", name:forIndexName, a:{type:NUM}} , + right:{a:{type:NUM}, tag:"literal", value:{tag:"num", value:1n}} } + // adding code for index = index+1 + nextAssign.push({tag:"assign", destruct: destructureAss, value: incrementIndexCall, a:{type:NONE} }) + } + break; + } + return [hasnextCall, nextAssign] +} + diff --git a/tests/forloops.test.ts b/tests/forloops.test.ts index d5ab3aa3..a8ae03aa 100644 --- a/tests/forloops.test.ts +++ b/tests/forloops.test.ts @@ -858,7 +858,7 @@ def checkMultiLoopFunction(min:int, max:int) ->int: print(checkMultiLoopFunction(1,6)) `, [`False`,`True`,`1`,`2`,`3`,`4`,`5`,`False`,`True`, `1`,`2`,`3`,`4`,`5`,`False`,`30`]); - +//30 assertPrint("generic for loop", ` T = TypeVar("T") @@ -899,7 +899,154 @@ None`, [`True`,`False`] ); +//31 +assertPrint("for-loop lists-int", +` +items:[int] = None +index:int = 0 +i:int = 0 + +items = [1,2,5,60] +for i in items: + print(i) +`, +[`1`,`2`,`5`,`60`] +); + +//32 +assertPrint("for-loop lists-bool", +` +items:[bool] = None +index:int = 0 +i:bool = True + +items = [True, False, True, False] +for i in items: + print(i) +`, +[`True`,`False`,`True`,`False`] +); + +//33 +assertPrint("for-loop lists-empty", +` +items:[bool] = None +index:int = 0 +i:bool = True +for i in items: + print(i) + +print(not(i)) +`,[`False`] +); + +//34 +assertPrint("for-loop lists-ifcondn", +` +items:[int] = None +index:int = 0 +i:int = 0 +items = [1,2,3,4] +for i in items: + if(i%2==0): + print(i) +`, +[`2`,`4`] +); + +//35 +assertPrint("for-loop lists-continue", +` +items:[int] = None +index:int = 0 +i:int = 0 +items = [1,2,3,4] +for i in items: + if(i%2==0): + continue + print(i) +`, +[`1`,`3`] +); + +//36 +assertPrint("for-loop lists-break", +` +items:[int] = None +index:int = 0 +i:int = 0 +items = [1,2,3,4] +for i in items: + print(i) + if(i%2==0): + break +`, +[`1`,`2`] +); + +//37 +assertPrint("for-loop lists-direct", +` +index:int = 0 +i:int = 0 +for i in [10,20,30,40]: + print(i) + +`, +[`10`,`20`,`30`,`40`] +); + +//38 +assertPrint("for-loop lists-function", +` +def returnList(n:int)->[int]: + return [n, n+1, n+2,n+3,n+4] +i:int = 0 +for i in returnList(9): + print(i) + +`, +[`9`,`10`,`11`,`12`,`13`] +); +//39 +assertPrint("for-loop lists-within-function", +` +def loopList(): + i:int = 0 + for i in [2,3,4,5]: + print(i) + +loopList() +`, +[`2`,`3`,`4`,`5`] +); + +//40 +assertPrint("for-loop lists-changingvalues", +` +i:int = 0 +items:[int] = None +items = [1,2,3,5] +for i in items: + print(i) + items = [2,3] +`, +[`1`,`2`,`3`,`5`] +); + +//41 +assertPrint("for-loop basic-nestedLists", +` +a :[[int]] = None +i:[int] = None +j:int = 0 +a = [[1,2,3,4], [10,20]] +for j in a[0]: + print(j) +`, +[`1`,`2`,`3`,`4`] +); }); \ No newline at end of file diff --git a/type-check.ts b/type-check.ts index 284c3ff6..3bc11de3 100644 --- a/type-check.ts +++ b/type-check.ts @@ -662,22 +662,8 @@ export function tcStmt(env: GlobalTypeEnv, locals: LocalTypeEnv, stmt: Stmt, tIterator : Type, SRC: string) :Expr { + + var tValObject = tcExpr(env, locals, stmtValues, SRC); + + switch(tValObject.a.type.tag){ + case "class": + { + if (!env.classes.has(tValObject.a.type.name)) + throw new TypeCheckError(SRC,"values on an unknown class"); + const [__, methods] = env.classes.get(tValObject.a.type.name); + if(!(methods.has("hasnext")) || methods.get("hasnext")[1].tag != BOOL.tag) + throw new TypeCheckError(SRC, "iterable class must have hasnext method with boolean return type"); + if(!(methods.has("next"))) { throw new TypeCheckError(SRC, "No next method"); } + const methodType = specializeMethodType(env, tValObject.a.type, methods.get("next")); + if(!equalType(methodType[1],tIterator)) { + throw new TypeCheckError(SRC, "iterable class must have next method with same return type as iterator"); + } + if(!(methods.has("reset")) || methods.get("reset")[1].tag != NONE.tag) + throw new TypeCheckError(SRC, "iterable class must have reset method with none return type"); + } + break + case "list": + if(!equalType(tValObject.a.type.itemType,tIterator)) { + throw new TypeCheckError(SRC, "List must be of same type as iterator"); + } + break + default: + throw new TypeCheckError(SRC,"Unsupported type of values"); + } + return tValObject +} + export function tcExpr(env: GlobalTypeEnv, locals: LocalTypeEnv, expr: Expr, SRC: string): Expr { switch (expr.tag) { case "literal": @@ -1041,3 +1060,4 @@ export function tcIterator(env : GlobalTypeEnv, locals : LocalTypeEnv, iterator: case "none": return NONE; } } +