Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for multiplying parentheses #87

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.5.1] - 2024-05-23

### Added

- Add support for multiplying parentheses

## [2.5.0] - 2024-04-16

### Added
Expand Down
5 changes: 5 additions & 0 deletions build/native_assets/macos/native_assets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
format-version:
- 1
- 0
- 0
native-assets: {}
Binary file not shown.
Binary file added build/unit_test_assets/AssetManifest.bin
Binary file not shown.
1 change: 1 addition & 0 deletions build/unit_test_assets/AssetManifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions build/unit_test_assets/FontManifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Binary file added build/unit_test_assets/NOTICES.Z
Binary file not shown.
Binary file added build/unit_test_assets/shaders/ink_sparkle.frag
Binary file not shown.
48 changes: 39 additions & 9 deletions lib/src/parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ class Parser {
Parser() : lex = Lexer();
Map<String, dynamic> functionHandlers = <String, dynamic>{};

/// Parses the given input string into an [Expression]. Throws a
/// [ArgumentError] if the given [inputString] is empty. Throws a
/// [StateError] if the token stream is invalid. Returns a valid
/// [Expression].
Expression parse(String inputString) {
/// Parses the given input string into an [Expression]. If
/// [multiplyWithParentheses] is true you can multiply using
/// parentheses. Throws [ArgumentError] if the given [inputString]
/// is empty. Throws a [StateError] if the token stream is
/// invalid. Returns a valid [Expression].
Expression parse(String inputString, {bool multiplyWithParentheses = false}) {
if (inputString.trim().isEmpty) {
throw FormatException('The given input string was empty.');
}

final List<Expression> exprStack = <Expression>[];
final List<Token> inputStream = lex.tokenizeToRPN(inputString);
final List<Token> inputStream = lex.tokenizeToRPN(
inputString,
multiplyWithParentheses: multiplyWithParentheses,
);

for (Token currToken in inputStream) {
Expression currExpr, left, right;
Expand Down Expand Up @@ -208,7 +212,8 @@ class Lexer {

/// Tokenizes a given input string.
/// Returns a list of [Token] in infix notation.
List<Token> tokenize(String inputString) {
List<Token> tokenize(String inputString,
{ bool multiplyWithParentheses=false}) {
final List<Token> tempTokenStream = <Token>[];
final String clearedString = inputString.replaceAll(' ', '').trim();
final RuneIterator iter = clearedString.runes.iterator;
Expand Down Expand Up @@ -299,6 +304,27 @@ class Lexer {
// There are no more symbols in the input string but there is still a variable or keyword in the varBuffer
_doVarBuffer(tempTokenStream);
}
if (multiplyWithParentheses) {
for (int i = 0; i < tempTokenStream.length; i++) {
if (tempTokenStream[i].type == TokenType.RBRACE &&
i != tempTokenStream.length - 1) {
final nextSymbol = tempTokenStream[i + 1];
if ([
TokenType.RBRACE,
TokenType.DIV,
TokenType.TIMES,
TokenType.MINUS,
TokenType.PLUS,
TokenType.MOD,
].every((element) => nextSymbol.type != element)) {
tempTokenStream.insert(
i + 1,
Token('*', TokenType.TIMES),
);
}
}
}
}
return tempTokenStream;
}

Expand Down Expand Up @@ -465,8 +491,12 @@ class Lexer {
/// This method invokes the createTokenStream methode to create an infix token
/// stream and then invokes the shunting yard method to transform this stream
/// into a RPN (reverse polish notation) token stream.
List<Token> tokenizeToRPN(String inputString) {
final List<Token> infixStream = tokenize(inputString);
List<Token> tokenizeToRPN(String inputString,
{bool multiplyWithParentheses = false}) {
final List<Token> infixStream = tokenize(
inputString,
multiplyWithParentheses: multiplyWithParentheses,
);
return shuntingYard(infixStream);
}
}
Expand Down
24 changes: 21 additions & 3 deletions test/parser_test_set.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ParserTests extends TestSet {
'Power': parsePower,
'Modulo': parseModulo,
'Multiplication': parseMultiplication,
'MultiplicationWithParentheses': parseMultiplicationWithParentheses,
'Division': parseDivision,
'Addition': parsePlus,
'Subtraction': parseMinus,
Expand All @@ -49,10 +50,19 @@ class ParserTests extends TestSet {

Parser parser = Parser();

void parameterized(Map<String, Expression> cases) {
void parameterized(Map<String, Expression> cases,
{bool multiplyWithParentheses = false}) {
cases.forEach((key, value) {
test('$key -> $value',
() => expect(parser.parse(key).toString(), value.toString()));
test(
'$key -> $value',
() => expect(
parser
.parse(
key,
multiplyWithParentheses: multiplyWithParentheses,
)
.toString(),
value.toString()));
});
}

Expand Down Expand Up @@ -127,6 +137,14 @@ class ParserTests extends TestSet {
parameterized(cases);
}

void parseMultiplicationWithParentheses() {
var cases = {
'(5)(5)': Number(5) * Number(5),
'(-2.0)5': -Number(2.0) * Number(5),
};
parameterized(cases,multiplyWithParentheses: true);
}

void parseDivision() {
var cases = {
'0 / 1': Number(0) / Number(1),
Expand Down