mirror of https://github.com/nocturn9x/nimkalc.git
Added $ operator in root package, minor formatting fixes
This commit is contained in:
parent
d0bd200428
commit
4330411843
|
@ -149,8 +149,8 @@ echo eval("2+2") # Prints Integer(4)
|
||||||
|
|
||||||
## Installing
|
## 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
|
__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
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Package
|
# Package
|
||||||
|
|
||||||
version = "0.2"
|
version = "0.2.1"
|
||||||
author = "Mattia Giambirtone"
|
author = "Mattia Giambirtone"
|
||||||
description = "An advanced parsing library for mathematical expressions and equations"
|
description = "An advanced parsing library for mathematical expressions and equations"
|
||||||
license = "Apache 2.0"
|
license = "Apache 2.0"
|
||||||
|
@ -8,4 +8,4 @@ srcDir = "src"
|
||||||
|
|
||||||
# Deps
|
# Deps
|
||||||
|
|
||||||
requires "nim >= 1.2.0"
|
requires "nim >= 1.2.0"
|
||||||
|
|
|
@ -12,14 +12,43 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
##
|
## Top-level module for nimkalc
|
||||||
|
|
||||||
import nimkalc/parsing/parser
|
import nimkalc/parsing/parser
|
||||||
import nimkalc/objects/ast
|
import nimkalc/objects/ast
|
||||||
import nimkalc/parsing/lexer
|
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
|
## Evaluates a mathematical expression as a string
|
||||||
## and returns a leaf node representing the result
|
## and returns a leaf node representing the result
|
||||||
let l = initLexer()
|
let l = initLexer()
|
||||||
|
|
|
@ -25,7 +25,7 @@ import strutils
|
||||||
type
|
type
|
||||||
NodeKind* {.pure.} = enum
|
NodeKind* {.pure.} = enum
|
||||||
# An enum for all kinds of AST nodes
|
# An enum for all kinds of AST nodes
|
||||||
Grouping, Unary, Binary, Integer,
|
Grouping, Unary, Binary, Integer,
|
||||||
Float, Call, Ident
|
Float, Call, Ident
|
||||||
AstNode* = ref object
|
AstNode* = ref object
|
||||||
# An AST node object
|
# An AST node object
|
||||||
|
@ -54,12 +54,7 @@ type
|
||||||
# A node visitor object
|
# A node visitor object
|
||||||
|
|
||||||
|
|
||||||
proc initNodeVisitor*(): NodeVisitor =
|
proc `$`*(self: AstNode): string =
|
||||||
## Initializes a node visitor
|
|
||||||
new(result)
|
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(self: AstNode): string =
|
|
||||||
## Stringifies an AST node
|
## Stringifies an AST node
|
||||||
case self.kind:
|
case self.kind:
|
||||||
of NodeKind.Grouping:
|
of NodeKind.Grouping:
|
||||||
|
@ -78,7 +73,13 @@ proc `$`*(self: AstNode): string =
|
||||||
result = &"Identifier({self.name})"
|
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
|
## Handy template that avoids us the hassle of copy-pasting
|
||||||
## the same checks over and over again in the visitor
|
## the same checks over and over again in the visitor
|
||||||
let r = operator(left.value, right.value)
|
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)
|
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
|
## Handy template to ensure that a given node's value is not 0
|
||||||
if node.value == 0.0:
|
if node.value == 0.0:
|
||||||
case node.kind:
|
case node.kind:
|
||||||
|
@ -99,7 +100,7 @@ template ensureNonZero(node: AstNode) =
|
||||||
raise newException(CatchableError, &"invalid node kind '{node.kind}' for ensureNonZero")
|
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
|
## Ensures both operands are integers
|
||||||
if left.kind != NodeKind.Integer or right.kind != NodeKind.Integer:
|
if left.kind != NodeKind.Integer or right.kind != NodeKind.Integer:
|
||||||
raise newException(MathError, "an integer is required")
|
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 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
|
## Implements the accept part of the visitor pattern
|
||||||
## for our AST visitor
|
## for our AST visitor
|
||||||
case self.kind:
|
case self.kind:
|
||||||
|
@ -129,7 +130,7 @@ proc accept(self: AstNode, visitor: NodeVisitor): AstNode =
|
||||||
result = visitor.visit_call(self)
|
result = visitor.visit_call(self)
|
||||||
|
|
||||||
|
|
||||||
proc eval*(self: NodeVisitor, node: AstNode): AstNode =
|
proc eval*(self: NodeVisitor, node: AstNode): AstNode =
|
||||||
## Evaluates an AST node
|
## Evaluates an AST node
|
||||||
result = node.accept(self)
|
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
|
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
|
## Visits function call expressions
|
||||||
var args: seq[AstNode] = @[]
|
var args: seq[AstNode] = @[]
|
||||||
for arg in node.arguments:
|
for arg in node.arguments:
|
||||||
args.add(self.eval(arg))
|
args.add(self.eval(arg))
|
||||||
if node.function.name == "sin":
|
if node.function.name == "sin":
|
||||||
let r = sin(args[0].value)
|
let r = sin(args[0].value)
|
||||||
if r is float:
|
if r is float:
|
||||||
result = AstNode(kind: NodeKind.Float, value: r)
|
result = AstNode(kind: NodeKind.Float, value: r)
|
||||||
else:
|
else:
|
||||||
result = AstNode(kind: NodeKind.Integer, value: float(r))
|
result = AstNode(kind: NodeKind.Integer, value: float(r))
|
||||||
if node.function.name == "cos":
|
if node.function.name == "cos":
|
||||||
let r = cos(args[0].value)
|
let r = cos(args[0].value)
|
||||||
if r is float:
|
if r is float:
|
||||||
result = AstNode(kind: NodeKind.Float, value: r)
|
result = AstNode(kind: NodeKind.Float, value: r)
|
||||||
else:
|
else:
|
||||||
result = AstNode(kind: NodeKind.Integer, value: float(r))
|
result = AstNode(kind: NodeKind.Integer, value: float(r))
|
||||||
if node.function.name == "tan":
|
if node.function.name == "tan":
|
||||||
let r = tan(args[0].value)
|
let r = tan(args[0].value)
|
||||||
if r is float:
|
if r is float:
|
||||||
result = AstNode(kind: NodeKind.Float, value: r)
|
result = AstNode(kind: NodeKind.Float, value: r)
|
||||||
else:
|
else:
|
||||||
result = AstNode(kind: NodeKind.Integer, value: float(r))
|
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
|
## Visits grouping (i.e. parenthesized) expressions. Parentheses
|
||||||
## have no other meaning than to allow a lower-precedence expression
|
## have no other meaning than to allow a lower-precedence expression
|
||||||
## where a higher-precedence one is expected so that 2 * (3 + 1) is
|
## 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)
|
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
|
## Visits a binary AST node and evaluates it
|
||||||
let right = self.eval(node.right)
|
let right = self.eval(node.right)
|
||||||
let left = self.eval(node.left)
|
let left = self.eval(node.left)
|
||||||
|
@ -197,7 +198,7 @@ proc visit_binary(self: NodeVisitor, node: AstNode): AstNode =
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
|
|
||||||
|
|
||||||
proc visit_unary(self: NodeVisitor, node: AstNode): AstNode =
|
proc visit_unary(self: NodeVisitor, node: AstNode): AstNode =
|
||||||
## Visits unary expressions and evaluates them
|
## Visits unary expressions and evaluates them
|
||||||
let expr = self.eval(node.operand)
|
let expr = self.eval(node.operand)
|
||||||
case node.unOp.kind:
|
case node.unOp.kind:
|
||||||
|
|
|
@ -33,5 +33,5 @@ type
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(self: Token): string =
|
proc `$`*(self: Token): string =
|
||||||
## Returns a string representation of self
|
## Returns a string representation of a token
|
||||||
result = &"Token({self.kind}, '{self.lexeme}')"
|
result = &"Token({self.kind}, '{self.lexeme}')"
|
||||||
|
|
Loading…
Reference in New Issue