diff --git a/src/Core/CsMic.Core.csproj b/src/Core/CSMic.Core.csproj
similarity index 91%
rename from src/Core/CsMic.Core.csproj
rename to src/Core/CSMic.Core.csproj
index e5cce08..b298d89 100644
--- a/src/Core/CsMic.Core.csproj
+++ b/src/Core/CSMic.Core.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/src/Core/cocor/Interpreter.atg b/src/Core/cocor/Interpreter.atg
index 2a641ba..b693d6e 100644
--- a/src/Core/cocor/Interpreter.atg
+++ b/src/Core/cocor/Interpreter.atg
@@ -58,6 +58,19 @@ bool IsArrayCall()
return false;
}
+// Implicit multiplication detection: the next token starts a Factor
+bool IsImplicitMul()
+{
+ // Allow whitespace between tokens; scanner already skips it
+ // A Factor/Value can start with: '(', identifier (incl. function/array), number, hex, or binary
+ if (la.kind == _LPAREN) return true;
+ if (la.kind == _identifier) return true;
+ if (la.kind == _number) return true;
+ if (la.kind == _hex) return true;
+ if (la.kind == _binary) return true;
+ return false;
+}
+
/*
* Character sets and tokens
*/
@@ -132,7 +145,8 @@ Term
=
(. decimal r1 = 0; r = 0; .)
Factor
- { '*' Factor (. r *= r1; .)
+ { IF(IsImplicitMul()) Factor (. r *= r1; .)
+ | '*' Factor (. r *= r1; .)
| '/' Factor (. r /= r1; .)
| '%' Term (. r %= r1; .)
}
diff --git a/src/StandardLibrary/CsMic.StandardLibrary.csproj b/src/StandardLibrary/CSMic.StandardLibrary.csproj
similarity index 100%
rename from src/StandardLibrary/CsMic.StandardLibrary.csproj
rename to src/StandardLibrary/CSMic.StandardLibrary.csproj
diff --git a/src/Tests/CSMic.Tests.csproj b/src/Tests/CSMic.Tests.csproj
index 77961d7..40e8a85 100644
--- a/src/Tests/CSMic.Tests.csproj
+++ b/src/Tests/CSMic.Tests.csproj
@@ -19,8 +19,8 @@
-
-
+
+
diff --git a/src/Tests/InputInterpreterTests.cs b/src/Tests/InputInterpreterTests.cs
index db0356c..c02af6f 100644
--- a/src/Tests/InputInterpreterTests.cs
+++ b/src/Tests/InputInterpreterTests.cs
@@ -158,4 +158,25 @@ public class InputInterpreterTests
_interp.Interpret("3+3");
Assert.That(_interp.LastExecutionTime, Is.GreaterThanOrEqualTo(TimeSpan.Zero));
}
+
+ [Test]
+ public void ImplicitMultiplication_Basic()
+ {
+ AssertSuccess(_interp.Interpret("2(3+1)"), 8, _interp);
+ }
+
+ [Test]
+ public void ImplicitMultiplication_WithVariableAndParens()
+ {
+ AssertSuccess(_interp.Interpret("x :: 3"), 3, _interp);
+ AssertSuccess(_interp.Interpret("2x"), 6, _interp);
+ AssertSuccess(_interp.Interpret("(x+1)(x-1)"), 8, _interp);
+ }
+
+ [Test]
+ public void ImplicitMultiplication_ChainedParens()
+ {
+ AssertSuccess(_interp.Interpret("x :: 4"), 4, _interp);
+ AssertSuccess(_interp.Interpret("3(x)(2)"), 24, _interp);
+ }
}
diff --git a/src/Tests/TrigonometryFunctionsTests.cs b/src/Tests/TrigonometryFunctionsTests.cs
index 4c2665f..4486d2d 100644
--- a/src/Tests/TrigonometryFunctionsTests.cs
+++ b/src/Tests/TrigonometryFunctionsTests.cs
@@ -63,6 +63,14 @@ public class TrigonometryFunctionsTests
AssertApprox(_interp.Interpret("atan(1)"), 0.7853981633974483m, 0.0000000000001m, _interp);
AssertApprox(_interp.Interpret("atan2(1, 0)"), 1.5707963267948966m, 0.0000000000001m, _interp);
}
+
+ [Test]
+ public void ImplicitMultiplication_WithFunctions()
+ {
+ AssertSuccess(_interp.Interpret("2sin(0)"), 0m, _interp);
+ AssertSuccess(_interp.Interpret("2 sin(0)"), 0m, _interp);
+ AssertApprox(_interp.Interpret("2sin(pi/2)"), 2m, 0.0000000000001m, _interp);
+ }
}
public class HyperbolicTrigFunctionsTests
@@ -95,4 +103,3 @@ public class HyperbolicTrigFunctionsTests
AssertSuccess(_interp.Interpret("atanh(0)"), 0m, _interp);
}
}
-