From 43304118436604640182fcc6712a267599a25e57 Mon Sep 17 00:00:00 2001 From: nocturn9x Date: Thu, 11 Mar 2021 22:29:59 +0100 Subject: [PATCH] Added $ operator in root package, minor formatting fixes --- README.md | 4 ++-- nimkalc.nimble | 4 ++-- src/nimkalc.nim | 33 +++++++++++++++++++++++++++-- src/nimkalc/objects/ast.nim | 39 ++++++++++++++++++----------------- src/nimkalc/objects/token.nim | 4 ++-- 5 files changed, 57 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 3eeff1e..1cbff70 100644 --- a/README.md +++ b/README.md @@ -149,8 +149,8 @@ echo eval("2+2") # Prints Integer(4) ## Installing -You can install the package via nimble with this command: `nimble install https://github.com/nocturn9x/nimkalc` +You can install the package via nimble with this command: `nimble install nimkalc` -__Note__: Nim 1.2.0 or higher is required to build NimKalc! Other versions are likely work if they're not too old, but they have not been tested \ No newline at end of file +__Note__: Nim 1.2.0 or higher is required to build NimKalc! Other versions are likely work if they're not too old, but they have not been tested diff --git a/nimkalc.nimble b/nimkalc.nimble index 940ffd6..50db629 100644 --- a/nimkalc.nimble +++ b/nimkalc.nimble @@ -1,6 +1,6 @@ # Package -version = "0.2" +version = "0.2.1" author = "Mattia Giambirtone" description = "An advanced parsing library for mathematical expressions and equations" license = "Apache 2.0" @@ -8,4 +8,4 @@ srcDir = "src" # Deps -requires "nim >= 1.2.0" \ No newline at end of file +requires "nim >= 1.2.0" diff --git a/src/nimkalc.nim b/src/nimkalc.nim index 73d2eec..bb48533 100644 --- a/src/nimkalc.nim +++ b/src/nimkalc.nim @@ -12,14 +12,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -## +## Top-level module for nimkalc import nimkalc/parsing/parser import nimkalc/objects/ast import nimkalc/parsing/lexer +import nimkalc/parsing/token -proc eval*(source: string): AstNode = +import strutils +import strformat + + +proc `$`*(self: AstNode): string = + ## Stringifies an AST node + case self.kind: + of NodeKind.Grouping: + result = &"Grouping({self.expr})" + of NodeKind.Unary: + result = &"Unary({$self.unOp.kind}, {$self.operand})" + of NodeKind.Binary: + result = &"Binary({$self.left}, {$self.binOp.kind}, {$self.right})" + of NodeKind.Integer: + result = &"Integer({$int(self.value)})" + of NodeKind.Float: + result = &"Float({$self.value})" + of NodeKind.Call: + result = &"Call({self.function.name}, {self.arguments})" + of NodeKind.Ident: + result = &"Identifier({self.name})" + + +proc `$`*(self: Token): string = + ## Returns a string representation of a token + result = &"Token({self.kind}, '{self.lexeme}')" + + +proc eval*(source: string): AstNode = ## Evaluates a mathematical expression as a string ## and returns a leaf node representing the result let l = initLexer() diff --git a/src/nimkalc/objects/ast.nim b/src/nimkalc/objects/ast.nim index e312026..a533168 100644 --- a/src/nimkalc/objects/ast.nim +++ b/src/nimkalc/objects/ast.nim @@ -25,7 +25,7 @@ import strutils type NodeKind* {.pure.} = enum # An enum for all kinds of AST nodes - Grouping, Unary, Binary, Integer, + Grouping, Unary, Binary, Integer, Float, Call, Ident AstNode* = ref object # An AST node object @@ -54,12 +54,7 @@ type # A node visitor object -proc initNodeVisitor*(): NodeVisitor = - ## Initializes a node visitor - new(result) - - -proc `$`*(self: AstNode): string = +proc `$`*(self: AstNode): string = ## Stringifies an AST node case self.kind: of NodeKind.Grouping: @@ -78,7 +73,13 @@ proc `$`*(self: AstNode): string = result = &"Identifier({self.name})" -template handleBinary(left, right: AstNode, operator: untyped): AstNode = + +proc initNodeVisitor*(): NodeVisitor = + ## Initializes a node visitor + new(result) + + +template handleBinary(left, right: AstNode, operator: untyped): AstNode = ## Handy template that avoids us the hassle of copy-pasting ## the same checks over and over again in the visitor let r = operator(left.value, right.value) @@ -89,7 +90,7 @@ template handleBinary(left, right: AstNode, operator: untyped): AstNode = AstNode(kind: NodeKind.Float, value: r) -template ensureNonZero(node: AstNode) = +template ensureNonZero(node: AstNode) = ## Handy template to ensure that a given node's value is not 0 if node.value == 0.0: case node.kind: @@ -99,7 +100,7 @@ template ensureNonZero(node: AstNode) = raise newException(CatchableError, &"invalid node kind '{node.kind}' for ensureNonZero") -template ensureIntegers(left, right: AstNode) = +template ensureIntegers(left, right: AstNode) = ## Ensures both operands are integers if left.kind != NodeKind.Integer or right.kind != NodeKind.Integer: raise newException(MathError, "an integer is required") @@ -113,7 +114,7 @@ proc visit_grouping(self: NodeVisitor, node: AstNode): AstNode proc visit_call(self: NodeVisitor, node: AstNode): AstNode -proc accept(self: AstNode, visitor: NodeVisitor): AstNode = +proc accept(self: AstNode, visitor: NodeVisitor): AstNode = ## Implements the accept part of the visitor pattern ## for our AST visitor case self.kind: @@ -129,7 +130,7 @@ proc accept(self: AstNode, visitor: NodeVisitor): AstNode = result = visitor.visit_call(self) -proc eval*(self: NodeVisitor, node: AstNode): AstNode = +proc eval*(self: NodeVisitor, node: AstNode): AstNode = ## Evaluates an AST node result = node.accept(self) @@ -139,32 +140,32 @@ proc visit_literal(self: NodeVisitor, node: AstNode): AstNode = result = node # Not that we can do anything else after all, lol -proc visit_call(self: NodeVisitor, node: AstNode): AstNode = +proc visit_call(self: NodeVisitor, node: AstNode): AstNode = ## Visits function call expressions var args: seq[AstNode] = @[] for arg in node.arguments: args.add(self.eval(arg)) if node.function.name == "sin": - let r = sin(args[0].value) + let r = sin(args[0].value) if r is float: result = AstNode(kind: NodeKind.Float, value: r) else: result = AstNode(kind: NodeKind.Integer, value: float(r)) if node.function.name == "cos": - let r = cos(args[0].value) + let r = cos(args[0].value) if r is float: result = AstNode(kind: NodeKind.Float, value: r) else: result = AstNode(kind: NodeKind.Integer, value: float(r)) if node.function.name == "tan": - let r = tan(args[0].value) + let r = tan(args[0].value) if r is float: result = AstNode(kind: NodeKind.Float, value: r) else: result = AstNode(kind: NodeKind.Integer, value: float(r)) -proc visit_grouping(self: NodeVisitor, node: AstNode): AstNode = +proc visit_grouping(self: NodeVisitor, node: AstNode): AstNode = ## Visits grouping (i.e. parenthesized) expressions. Parentheses ## have no other meaning than to allow a lower-precedence expression ## where a higher-precedence one is expected so that 2 * (3 + 1) is @@ -172,7 +173,7 @@ proc visit_grouping(self: NodeVisitor, node: AstNode): AstNode = return self.eval(node.expr) -proc visit_binary(self: NodeVisitor, node: AstNode): AstNode = +proc visit_binary(self: NodeVisitor, node: AstNode): AstNode = ## Visits a binary AST node and evaluates it let right = self.eval(node.right) let left = self.eval(node.left) @@ -197,7 +198,7 @@ proc visit_binary(self: NodeVisitor, node: AstNode): AstNode = discard # Unreachable -proc visit_unary(self: NodeVisitor, node: AstNode): AstNode = +proc visit_unary(self: NodeVisitor, node: AstNode): AstNode = ## Visits unary expressions and evaluates them let expr = self.eval(node.operand) case node.unOp.kind: diff --git a/src/nimkalc/objects/token.nim b/src/nimkalc/objects/token.nim index ac52109..814adf8 100644 --- a/src/nimkalc/objects/token.nim +++ b/src/nimkalc/objects/token.nim @@ -33,5 +33,5 @@ type proc `$`*(self: Token): string = - ## Returns a string representation of self - result = &"Token({self.kind}, '{self.lexeme}')" \ No newline at end of file + ## Returns a string representation of a token + result = &"Token({self.kind}, '{self.lexeme}')"