From 7da2c9f87913ce5c79efaeff22d3adbc450895d9 Mon Sep 17 00:00:00 2001 From: Ethosa Date: Wed, 17 Apr 2024 07:32:50 +0700 Subject: [PATCH] upgrade generators --- src/elyspkg/ast.nim | 118 +++++++++++++++++++++++++++++++++++++++++ src/elyspkg/parser.nim | 25 ++++++++- src/elyspkg/result.nim | 11 +++- tests/test.nim | 4 ++ 4 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/elyspkg/ast.nim b/src/elyspkg/ast.nim index c5c9e5f..10a2e25 100644 --- a/src/elyspkg/ast.nim +++ b/src/elyspkg/ast.nim @@ -127,6 +127,9 @@ template whileStmt*(c: ASTRoot, b: ASTRoot): WhileStmt = template forInStmt*(v: seq[ASTRoot], o, b: ASTRoot): ForInStmt = ForInStmt(vars: v, obj: o, body: b, kind: akForInStmt) +template forInGen*(v: seq[ASTRoot], o, b: ASTRoot, c: Option[ASTRoot]): ForInGenerator = + ForInGenerator(vars: v, obj: o, body: b, condition: c, kind: akForInStmt) + template funcStmt*(n: string, a: ArrayAST, kwa: ObjectAST, b: ASTRoot): FuncStmt = FuncStmt(name: n, args: a, kwargs: kwa, body: b, kind: akFunc) @@ -309,12 +312,32 @@ func `/`*(l, r: ASTRoot, env: Environment): ASTRoot = a = l.eval(env) b = r.eval(env) if a.kind == akInt and b.kind == akInt: + if b.IntAST.val == 0: + zeroDivisionError( + "Can not divide by 0", + l.line, l.col, l.code, l.filepath + ) return floatAST(a.IntAST.val / b.IntAST.val) elif a.kind == akInt and b.kind == akFloat: + if b.FloatAST.val == 0.0: + zeroDivisionError( + "Can not divide by 0", + l.line, l.col, l.code, l.filepath + ) return floatAst(a.IntAST.val.float / b.FloatAST.val) elif a.kind == akFloat and b.kind == akInt: + if b.IntAST.val == 0: + zeroDivisionError( + "Can not divide by 0", + l.line, l.col, l.code, l.filepath + ) return floatAst(a.FloatAST.val / b.IntAST.val.float) elif a.kind == akFloat and b.kind == akFloat: + if b.FloatAST.val == 0.0: + zeroDivisionError( + "Can not divide by 0", + l.line, l.col, l.code, l.filepath + ) return floatAst(a.FloatAST.val / b.FloatAST.val) else: valueError( @@ -328,8 +351,18 @@ func `//`*(l, r: ASTRoot, env: Environment): ASTRoot = a = l.eval(env) b = r.eval(env) if a.kind == akInt and b.kind == akInt: + if b.IntAST.val == 0: + zeroDivisionError( + "Can not divide by 0", + l.line, l.col, l.code, l.filepath + ) return intAST(a.IntAST.val div b.IntAST.val) elif a.kind == akInt and b.kind == akFloat: + if b.IntAST.val == 0: + zeroDivisionError( + "Can not divide by 0", + l.line, l.col, l.code, l.filepath + ) return intAst(a.FloatAST.val.int div b.IntAST.val) else: valueError( @@ -918,3 +951,88 @@ method eval*(self: ForInStmt, env: Environment): ASTRoot = self.line, self.col, self.code, self.filepath ) return res + +method eval*(self: ForInGenerator, env: Environment): ASTRoot = + let + obj = self.obj.eval(env) + var + environment = newEnv(env) + res = arrAst(@[]) + # check for variables + for i in self.vars: + if i.kind != akVar: + valueError( + "Can not iterate over " & obj.astValue(environment) & + " via " & i.astValue(environment), + self.line, self.col, self.code, self.filepath + ) + case self.vars.len: + of 1: + let variable = self.vars[0].VarAST.name + case obj.kind: + of akArr: + for i in obj.ArrayAST.val: + environment.setDef(variable, i.eval(environment).ASTExpr) + let x = self.body.eval(environment) + if self.condition.isNone: + res.val.add x + elif self.condition.get.eval(environment): + res.val.add x + of akSliceExpr: + for i in obj.SliceExprAST.l.IntAST.val..obj.SliceExprAST.r.IntAST.val: + environment.setDef(variable, intAst(i)) + let x = self.body.eval(environment) + if self.condition.isNone: + res.val.add x + elif self.condition.get.eval(environment): + res.val.add x + of akString: + for i in obj.StringAST.val: + environment.setDef(variable, stringAst($i)) + let x = self.body.eval(environment) + if self.condition.isNone: + res.val.add x + elif self.condition.get.eval(environment): + res.val.add x + else: + valueError( + "Can not unpack " & $self.vars.len & " variables from " & + obj.astValue(env), + self.line, self.col, self.code, self.filepath + ) + of 2: + let + variable1 = self.vars[0].VarAST.name + variable2 = self.vars[1].VarAST.name + case obj.kind: + of akArr: + for (index, value) in obj.ArrayAST.val.pairs: + environment.setDef(variable2, value.eval(environment).ASTExpr) + environment.setDef(variable1, intAst(index)) + let x = self.body.eval(environment) + if self.condition.isNone: + res.val.add x + elif self.condition.get.eval(environment): + res.val.add x + of akString: + for (index, value) in obj.StringAST.val.pairs: + environment.setDef(variable2, stringAst($value)) + environment.setDef(variable1, intAst(index)) + let x = self.body.eval(environment) + if self.condition.isNone: + res.val.add x + elif self.condition.get.eval(environment): + res.val.add x + else: + valueError( + "Can not unpack " & $self.vars.len & " variables from " & + obj.astValue(env), + self.line, self.col, self.code, self.filepath + ) + else: + valueError( + "Can not unpack " & $self.vars.len & " variables from " & + obj.astValue(env), + self.line, self.col, self.code, self.filepath + ) + return res diff --git a/src/elyspkg/parser.nim b/src/elyspkg/parser.nim index 57eced0..db5e335 100644 --- a/src/elyspkg/parser.nim +++ b/src/elyspkg/parser.nim @@ -200,8 +200,6 @@ func castFuncExpr(): Combinator = func processStringFuncExpr(res: Result): Option[Result] = - {.noSideEffect.}: - echo res astRes( callAst(res.valx.val.get, arrAst(@[res.valy.ast]), objAst(@[])), res.line, res.col, res.source, res.filepath @@ -234,12 +232,35 @@ func methodCallExpr(): Combinator = ) ^ processMethodCallExpr +func processForInGenerator(res: Result): Option[Result] = + var arr: seq[ASTRoot] = @[] + for i in res.valx.valx.valx.valy.arr: + arr.add i.ast + var condition = none[ASTRoot]() + if res.valy.kind == rkPair: + condition = res.valy.valy.ast.some + astRes(forInGen( + arr, res.valx.valy.ast, res.valx.valx.valx.valx.valx.ast, condition + ), res.line, res.col, res.source, res.filepath) +func forInGenerator(): Combinator = + ( + (operator"[" + ( + lazy(expr) + keyword"for" + repSep(idTag() ^ processVar, operator",") + + operator"in" + lazy(expr) + opt(keyword"if" + alt( + (operator"(" + lazy(expr) + operator")"), + lazy(expr) + )) + ) + operator"]") ^ processGroup + ) ^ processForInGenerator + + func exprTermPre(): Combinator = ( exprGroup() | lazy(ifStatement) | lazy(stmtListEmbed) | lazy(exprArray) | + lazy(forInGenerator) | incDecStatement() | unaryOperatorStmt() | bExprNot() | diff --git a/src/elyspkg/result.nim b/src/elyspkg/result.nim index 0f048c7..6d95bf3 100644 --- a/src/elyspkg/result.nim +++ b/src/elyspkg/result.nim @@ -11,6 +11,7 @@ type akEof, akStmt, akStmtList, akAssign, akPrint, akIncDec, akElifBranch, akElseBranch, akIfStmt, akBreak, akContinue, akWhile, akAssignBracket, akSwap, akForInStmt, + akForInGen, akFunc ASTRoot* = ref object of RootObj kind*: ASTKind @@ -133,6 +134,11 @@ type vars*: seq[ASTRoot] obj*: ASTRoot body*: ASTRoot + ForInGenerator* = ref object of Stmt + vars*: seq[ASTRoot] + obj*: ASTRoot + body*: ASTRoot + condition*: Option[ASTRoot] PrintStmt* = ref object of Stmt data*: seq[Result] @@ -204,7 +210,10 @@ func `$`*(ast: ASTRoot): string = $ast.SwapStmt.toL & ", " & $ast.SwapStmt.toR & ")" of akForInStmt: "ForInStmt( (" & ast.ForInStmt.vars.join(", ") & ") in " & $ast.ForInStmt.obj & - $ast.ForInStmt.body & ")" + ", " & $ast.ForInStmt.body & ")" + of akForInGen: + "ForInGenerator( (" & ast.ForInGenerator.vars.join(", ") & ") in " & $ast.ForInGenerator.obj & + ", " & $ast.ForInGenerator.condition & ")" else: "ASTRoot(kind: " & $ast.kind & ")" diff --git a/tests/test.nim b/tests/test.nim index 6c16cc2..1cab26c 100644 --- a/tests/test.nim +++ b/tests/test.nim @@ -217,4 +217,8 @@ suite "Elys": } } } + # here python-like generators + print [i for i in 0..10] + print [i for i in 0..10 if i % 2] + print [i*2 for i in 0..10 if i % 2] """