Compare commits
10 Commits
f27939ee67
...
d83106358f
| Author | SHA1 | Date | |
|---|---|---|---|
| d83106358f | |||
| 43d38645a5 | |||
| 7dab069aa6 | |||
| 02a552767b | |||
| e30cfc6cf4 | |||
| 2a4f92fe35 | |||
| 8fe03105b7 | |||
|
|
5907457907 | ||
| 726f4f2425 | |||
| 713784e783 |
@ -1,10 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<RollForward>LatestMajor</RollForward>
|
<LangVersion>latest</LangVersion>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="out\MainClass.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
12
FusionCalculator.exe.csproj
Normal file
12
FusionCalculator.exe.csproj
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<AssemblyName>FusionCalculator</AssemblyName>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# FusionCalculator
|
||||||
|
The calculator written in [Fusion language](https://github.com/fusionlanguage/fut). Can be translated to C and C#. Can be compiled as executable or library.
|
||||||
|
|
||||||
|
### Building
|
||||||
|
Requirements: fut, bash, dotnet8 (for c#), gcc (for C)
|
||||||
|
|
||||||
|
Just generate C# source files:
|
||||||
|
```shell
|
||||||
|
./build_cs.sh --translate-only
|
||||||
|
```
|
||||||
|
Build C# executable:
|
||||||
|
```shell
|
||||||
|
./build_cs.sh
|
||||||
|
```
|
||||||
|
Build C executable:
|
||||||
|
```shell
|
||||||
|
./build_c.sh
|
||||||
|
```
|
||||||
|
Build C executable with debug symbols:
|
||||||
|
```shell
|
||||||
|
./build_c.sh --debug
|
||||||
|
```
|
||||||
|
|
||||||
|
## Executable usage
|
||||||
|
Just call exe file in bin/ with math expression arguments:
|
||||||
|
```shell
|
||||||
|
bin/FusionCalculator.exe 1+2
|
||||||
|
3
|
||||||
|
bin/FusionCalculator.exe '11/(99-88)'
|
||||||
|
1
|
||||||
|
bin/FusionCalculator.exe '-1+6*(-2)'
|
||||||
|
-13
|
||||||
|
```
|
||||||
|
|
||||||
|
## Library usage
|
||||||
|
The public interface is very simple:
|
||||||
|
```cs
|
||||||
|
namespace FusionCalculator {
|
||||||
|
public static class Calculator {
|
||||||
|
public static double Calculate(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Just call Calculate and get the result!
|
||||||
24
build_c.sh
Normal file → Executable file
24
build_c.sh
Normal file → Executable file
@ -6,12 +6,30 @@ BIN_FILE="bin/FusionCalculator.exe"
|
|||||||
SRC_FILES="$(find src/ -name '*.fu')"
|
SRC_FILES="$(find src/ -name '*.fu')"
|
||||||
WARNINGS="-Wall -Wno-unused-value -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers"
|
WARNINGS="-Wall -Wno-unused-value -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers"
|
||||||
INCLUDES="$(pkg-config --cflags glib-2.0)"
|
INCLUDES="$(pkg-config --cflags glib-2.0)"
|
||||||
COMPILER_ARGS="-O0 -g"
|
|
||||||
|
if [[ $1 == '--debug' ]]; then
|
||||||
|
COMPILER_ARGS="-O0 -g"
|
||||||
|
else
|
||||||
|
COMPILER_ARGS="-O2"
|
||||||
|
fi
|
||||||
LINKER_ARGS="$(pkg-config --libs glib-2.0) -lm"
|
LINKER_ARGS="$(pkg-config --libs glib-2.0) -lm"
|
||||||
|
|
||||||
rm -rf out bin
|
rm -rf out bin
|
||||||
mkdir out bin
|
mkdir out bin
|
||||||
|
|
||||||
fut -l c -D C -o "$OUT_FILE" $SRC_FILES
|
echo "------------[fut]------------"
|
||||||
|
|
||||||
gcc $WARNINGS $COMPILER_ARGS "$OUT_FILE" -o "$BIN_FILE" $INCLUDES $LINKER_ARGS
|
args="-l c -D C -o "$OUT_FILE" $SRC_FILES"
|
||||||
|
if [[ $1 == '--implement-math-functions' || $2 == '--implement-math-functions' ]]; then
|
||||||
|
args="$args -D IMPLEMENT_MATH_FUNCTIONS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo fut $args
|
||||||
|
fut $args
|
||||||
|
|
||||||
|
if [[ $1 != '--translate-only' && $2 != '--translate-only' ]]; then
|
||||||
|
echo "------------[gcc]------------"
|
||||||
|
args="$WARNINGS $COMPILER_ARGS "$OUT_FILE" -o "$BIN_FILE" $INCLUDES $LINKER_ARGS"
|
||||||
|
echo gcc $args
|
||||||
|
gcc $args
|
||||||
|
fi
|
||||||
15
build_cs.sh
Normal file → Executable file
15
build_cs.sh
Normal file → Executable file
@ -12,10 +12,19 @@ done
|
|||||||
for src_file in $SRC_FILES; do
|
for src_file in $SRC_FILES; do
|
||||||
echo "---------[$src_file]---------"
|
echo "---------[$src_file]---------"
|
||||||
out_file="out/$(basename $src_file .fu).cs"
|
out_file="out/$(basename $src_file .fu).cs"
|
||||||
|
|
||||||
args="-l cs -D CS -n FusionCalculator $INCLUDES -o $out_file $src_file"
|
args="-l cs -D CS -n FusionCalculator $INCLUDES -o $out_file $src_file"
|
||||||
echo "fu $args"
|
if [[ $1 == '--implement-math-functions' || $2 == '--implement-math-functions' ]]; then
|
||||||
|
args="$args -D IMPLEMENT_MATH_FUNCTIONS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo fut $args
|
||||||
fut $args
|
fut $args
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "---------[FusionCalculator.csproj]---------"
|
if [[ $1 != '--translate-only' && $2 != '--translate-only' ]]; then
|
||||||
dotnet build FusionCalculator.csproj -o bin
|
echo "---------[FusionCalculator.csproj]---------"
|
||||||
|
args="build -c Release FusionCalculator.exe.csproj -o bin"
|
||||||
|
echo dotnet $args
|
||||||
|
dotnet $args
|
||||||
|
fi
|
||||||
|
|||||||
@ -11,13 +11,43 @@ abstract class FunctionCallExpression : IExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCallExpressionSin : FunctionCallExpression {
|
class FunctionCallExpressionSin : FunctionCallExpression {
|
||||||
internal override double FunctionImplementation(double x) => Math.Sin(x);
|
internal override double FunctionImplementation(double x) {
|
||||||
|
#if IMPLEMENT_MATH_FUNCTIONS
|
||||||
|
return MyMath.Sin(x);
|
||||||
|
#else
|
||||||
|
return Math.Sin(x);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCallExpressionCos : FunctionCallExpression {
|
class FunctionCallExpressionCos : FunctionCallExpression {
|
||||||
internal override double FunctionImplementation(double x) => Math.Cos(x);
|
internal override double FunctionImplementation(double x) {
|
||||||
|
#if IMPLEMENT_MATH_FUNCTIONS
|
||||||
|
return MyMath.Cos(x);
|
||||||
|
#else
|
||||||
|
return Math.Cos(x);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
class FunctionCallExpressionTan : FunctionCallExpression {
|
|
||||||
internal override double FunctionImplementation(double x) => Math.Tan(x);
|
class FunctionCallExpressionTg : FunctionCallExpression {
|
||||||
|
internal override double FunctionImplementation(double x){
|
||||||
|
#if IMPLEMENT_MATH_FUNCTIONS
|
||||||
|
return MyMath.Tg(x);
|
||||||
|
#else
|
||||||
|
return Math.Tan(x);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FunctionCallExpressionCtg : FunctionCallExpression {
|
||||||
|
internal override double FunctionImplementation(double x) {
|
||||||
|
#if IMPLEMENT_MATH_FUNCTIONS
|
||||||
|
return MyMath.Ctg(x);
|
||||||
|
#else
|
||||||
|
return 1 / Math.Tan(x);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FunctionCallExpressionAsin : FunctionCallExpression {
|
class FunctionCallExpressionAsin : FunctionCallExpression {
|
||||||
@ -26,10 +56,13 @@ class FunctionCallExpressionAsin : FunctionCallExpression {
|
|||||||
class FunctionCallExpressionAcos : FunctionCallExpression {
|
class FunctionCallExpressionAcos : FunctionCallExpression {
|
||||||
internal override double FunctionImplementation(double x) => Math.Acos(x);
|
internal override double FunctionImplementation(double x) => Math.Acos(x);
|
||||||
}
|
}
|
||||||
class FunctionCallExpressionAtan : FunctionCallExpression {
|
class FunctionCallExpressionAtg : FunctionCallExpression {
|
||||||
internal override double FunctionImplementation(double x) => Math.Atan(x);
|
internal override double FunctionImplementation(double x) => Math.Atan(x);
|
||||||
}
|
}
|
||||||
|
class FunctionCallExpressionActg : FunctionCallExpression {
|
||||||
|
internal override double FunctionImplementation(double x) => Math.Atan(1 / x);
|
||||||
|
}
|
||||||
|
|
||||||
class FunctionCallExpressionLog : FunctionCallExpression{
|
class FunctionCallExpressionLn : FunctionCallExpression{
|
||||||
internal override double FunctionImplementation(double x) => Math.Log(x);
|
internal override double FunctionImplementation(double x) => Math.Log(x);
|
||||||
}
|
}
|
||||||
|
|||||||
50
src/Expressions/MyMath.fu
Normal file
50
src/Expressions/MyMath.fu
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// My implementation of math functions using Taylor (Maclaurin) series
|
||||||
|
// https://en.wikipedia.org/wiki/Taylor_series#List_of_Maclaurin_series_of_some_common_functions
|
||||||
|
//
|
||||||
|
public class MyMath {
|
||||||
|
/// TODO: fix fail on tg(pi/2), tg(pi*), ctg(pi*2)
|
||||||
|
static double ClampRadians(double x){
|
||||||
|
int quotient = 0;
|
||||||
|
double pi2 = 2*Math.PI;
|
||||||
|
native {
|
||||||
|
quotient = (int)(x / pi2);
|
||||||
|
}
|
||||||
|
x -= pi2 * quotient;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Sin(double x){
|
||||||
|
x = ClampRadians(x);
|
||||||
|
int iters = 16;
|
||||||
|
double pow = x;
|
||||||
|
double fact = 1;
|
||||||
|
double result = x;
|
||||||
|
for(int i = 3; i <= (2*iters+1); i+=2){
|
||||||
|
pow *= x*x; // x power +2
|
||||||
|
fact *= i * (i-1); // making i! from (i-2)!
|
||||||
|
fact *= -1; // change sign every iteration
|
||||||
|
result += pow/fact;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Cos(double x){
|
||||||
|
x = ClampRadians(x);
|
||||||
|
int iters = 16;
|
||||||
|
double pow = 1;
|
||||||
|
double fact = 1;
|
||||||
|
double result = 1;
|
||||||
|
for(int i = 2; i <= (2*iters); i+=2){
|
||||||
|
pow *= x*x; // x power +2
|
||||||
|
fact *= i * (i-1); // making i! from (i-2)!
|
||||||
|
fact *= -1; // change sign every iteration
|
||||||
|
result += pow/fact;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double Tg(double x) => Sin(x)/Cos(x);
|
||||||
|
|
||||||
|
public static double Ctg(double x) => Cos(x)/Sin(x);
|
||||||
|
}
|
||||||
@ -29,17 +29,26 @@ class OperatorExpressionDiv : OperatorExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class OperatorExpressionMod : OperatorExpression {
|
class OperatorExpressionMod : OperatorExpression {
|
||||||
// returns if b>0 then returns a%b else returns a
|
|
||||||
internal override double OperatorImplementation(double a, double b) {
|
internal override double OperatorImplementation(double a, double b) {
|
||||||
if(b <= 0)
|
if(a == 0)
|
||||||
|
return b;
|
||||||
|
if(b == 0)
|
||||||
return a;
|
return a;
|
||||||
if(a > 0){
|
if(a > 0){
|
||||||
while(a >= b)
|
if(b > 0)
|
||||||
|
while(a-b >= 0)
|
||||||
a -= b;
|
a -= b;
|
||||||
|
else
|
||||||
|
while(a+b >= 0)
|
||||||
|
a += b;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while(a <= b)
|
if(b > 0)
|
||||||
|
while(a+b <= 0)
|
||||||
a += b;
|
a += b;
|
||||||
|
else
|
||||||
|
while(a-b <= 0)
|
||||||
|
a -= b;
|
||||||
}
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/Lexer.fu
24
src/Lexer.fu
@ -37,7 +37,7 @@ class Lexer {
|
|||||||
internal List<Token()> Lex!(string exprStr){
|
internal List<Token()> Lex!(string exprStr){
|
||||||
ExprStr = exprStr;
|
ExprStr = exprStr;
|
||||||
|
|
||||||
while (i < ExprStr.Length) {
|
for(i=0; i < ExprStr.Length; i++) {
|
||||||
switch (ExprStr[i]) {
|
switch (ExprStr[i]) {
|
||||||
// end token, add new predifined token and move next
|
// end token, add new predifined token and move next
|
||||||
case '(': AddStaticToken(TokBracketOpen); break;
|
case '(': AddStaticToken(TokBracketOpen); break;
|
||||||
@ -47,21 +47,33 @@ class Lexer {
|
|||||||
case '%': AddStaticToken(TokMod); break;
|
case '%': AddStaticToken(TokMod); break;
|
||||||
case '/': AddStaticToken(TokDiv); break;
|
case '/': AddStaticToken(TokDiv); break;
|
||||||
case '+': AddStaticToken(TokAdd); break;
|
case '+': AddStaticToken(TokAdd); break;
|
||||||
case '-': AddStaticToken(TokSub); break;
|
case '-':
|
||||||
|
// if '-' is not the first char and previous char isn't '(' or 'e' or 'E'
|
||||||
|
if(i != 0 && ExprStr[i-1] != '('
|
||||||
|
&& ExprStr[i-1] != 'e' && ExprStr[i-1] != 'E')
|
||||||
|
AddStaticToken(TokSub);
|
||||||
|
// else '-' is a part of numeric expression
|
||||||
|
break;
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
// if token starts with 'E' it is a literal
|
||||||
|
if(i == tokBegin)
|
||||||
|
tokType = Token.Type_Literal;
|
||||||
|
// else 'E' is a part of literal or number (sientific notation)
|
||||||
|
break;
|
||||||
// try end token and skip current char
|
// try end token and skip current char
|
||||||
case ' ': case '\t': case '\n': case '\r':
|
case ' ': case '\t': case '\n': case '\r':
|
||||||
TryEndToken();
|
TryEndToken();
|
||||||
i++;
|
tokBegin++;
|
||||||
break;
|
break;
|
||||||
// move next
|
// move next
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
i++;
|
case '.':
|
||||||
break;
|
break;
|
||||||
// set type from Numeric to Literal
|
// set type from Numeric to Literal
|
||||||
default:
|
default:
|
||||||
tokType = Token.Type_Literal;
|
tokType = Token.Type_Literal;
|
||||||
i++;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +98,6 @@ class Lexer {
|
|||||||
TokenStorage.Add();
|
TokenStorage.Add();
|
||||||
TokenStorage[TokenStorage.Count-1] = tok;
|
TokenStorage[TokenStorage.Count-1] = tok;
|
||||||
tokType = Token.Type_Number;
|
tokType = Token.Type_Number;
|
||||||
tokBegin = ++i;
|
tokBegin = i+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,14 +2,15 @@ public static class MainClass {
|
|||||||
public static void Main(string[] args){
|
public static void Main(string[] args){
|
||||||
#if CS
|
#if CS
|
||||||
native {
|
native {
|
||||||
System.Globalization.CultureInfo.DefaultThreadCurrentCulture = System.Globalization.CultureInfo.InvariantCulture;
|
System.Globalization.CultureInfo.DefaultThreadCurrentCulture =
|
||||||
|
System.Globalization.CultureInfo.InvariantCulture;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
string() joined = "";
|
string() joined = "";
|
||||||
foreach(string arg in args){
|
foreach(string arg in args){
|
||||||
joined += arg;
|
joined += arg + " ";
|
||||||
}
|
}
|
||||||
double rezult = Calculator.Calculate(joined);
|
double result = Calculator.Calculate(joined);
|
||||||
Console.WriteLine(rezult);
|
Console.WriteLine(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,11 +2,9 @@ class Parser {
|
|||||||
List<Token()> TokenStorage;
|
List<Token()> TokenStorage;
|
||||||
IExpression# RootExpression;
|
IExpression# RootExpression;
|
||||||
TokenLinkedList() TokensInRPN;
|
TokenLinkedList() TokensInRPN;
|
||||||
Token() TokZero;
|
|
||||||
|
|
||||||
internal Parser(){
|
internal Parser(){
|
||||||
RootExpression = new NumericExpression(); // NaN
|
RootExpression = new NumericExpression(); // NaN
|
||||||
TokZero = Token.Create("0", 0, 1, Token.Type_Number);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IExpression# Parse!(List<Token()> tokens){
|
internal IExpression# Parse!(List<Token()> tokens){
|
||||||
@ -26,8 +24,6 @@ class Parser {
|
|||||||
// Implementation of https://en.wikipedia.org/wiki/Shunting_yard_algorithm
|
// Implementation of https://en.wikipedia.org/wiki/Shunting_yard_algorithm
|
||||||
void SortTokensInRPN!(){
|
void SortTokensInRPN!(){
|
||||||
Stack<Token>() RPNStack;
|
Stack<Token>() RPNStack;
|
||||||
// is needed for negative numbers recognition
|
|
||||||
int prevTokType = Token.Type_BracketOpen;
|
|
||||||
|
|
||||||
for(int i = 0; i < TokenStorage.Count; i++){
|
for(int i = 0; i < TokenStorage.Count; i++){
|
||||||
Token tok = TokenStorage[i];
|
Token tok = TokenStorage[i];
|
||||||
@ -43,8 +39,7 @@ class Parser {
|
|||||||
case Token.Type_OperatorMod:
|
case Token.Type_OperatorMod:
|
||||||
case Token.Type_OperatorAdd:
|
case Token.Type_OperatorAdd:
|
||||||
case Token.Type_OperatorSub:
|
case Token.Type_OperatorSub:
|
||||||
if(type == Token.Type_OperatorSub && prevTokType == Token.Type_BracketOpen)
|
case Token.Type_Literal:
|
||||||
TokensInRPN.AddToEnd(TokZero);
|
|
||||||
while(RPNStack.Count != 0 && RPNStack.Peek().GetTokType() >= type){
|
while(RPNStack.Count != 0 && RPNStack.Peek().GetTokType() >= type){
|
||||||
Token op2 = RPNStack.Pop();
|
Token op2 = RPNStack.Pop();
|
||||||
TokensInRPN.AddToEnd(op2);
|
TokensInRPN.AddToEnd(op2);
|
||||||
@ -62,15 +57,10 @@ class Parser {
|
|||||||
TokensInRPN.AddToEnd(op2);
|
TokensInRPN.AddToEnd(op2);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Token.Type_Literal:
|
|
||||||
ThrowError($"token '{tok.GetStr()}' isn't implemented");
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
ThrowError($"unexpected token type '{type}'");
|
ThrowError($"unexpected token type '{type}'");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
prevTokType = type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add remaining operators
|
// add remaining operators
|
||||||
@ -88,30 +78,31 @@ class Parser {
|
|||||||
while(tokenNode != null){
|
while(tokenNode != null){
|
||||||
Token tok = tokenNode.GetValue();
|
Token tok = tokenNode.GetValue();
|
||||||
int type = tok.GetTokType();
|
int type = tok.GetTokType();
|
||||||
|
string() str = tok.GetStr();
|
||||||
|
|
||||||
switch(type){
|
switch(type){
|
||||||
case Token.Type_Number:
|
case Token.Type_Number:
|
||||||
NumericExpression# num = new NumericExpression();
|
NumericExpression# num = new NumericExpression();
|
||||||
num.Init(StringToDouble(tok.GetStr()));
|
num.Init(StringToDouble(str));
|
||||||
expressionStack.Push(num);
|
expressionStack.Push(num);
|
||||||
break;
|
break;
|
||||||
case Token.Type_OperatorPow:
|
case Token.Type_OperatorPow:
|
||||||
PushOperatorExpression(expressionStack, tok, new OperatorExpressionPow());
|
PushOperatorExpression(expressionStack, str, new OperatorExpressionPow());
|
||||||
break;
|
break;
|
||||||
case Token.Type_OperatorMul:
|
case Token.Type_OperatorMul:
|
||||||
PushOperatorExpression(expressionStack, tok, new OperatorExpressionMul());
|
PushOperatorExpression(expressionStack, str, new OperatorExpressionMul());
|
||||||
break;
|
break;
|
||||||
case Token.Type_OperatorDiv:
|
case Token.Type_OperatorDiv:
|
||||||
PushOperatorExpression(expressionStack, tok, new OperatorExpressionDiv());
|
PushOperatorExpression(expressionStack, str, new OperatorExpressionDiv());
|
||||||
break;
|
break;
|
||||||
case Token.Type_OperatorMod:
|
case Token.Type_OperatorMod:
|
||||||
PushOperatorExpression(expressionStack, tok, new OperatorExpressionMod());
|
PushOperatorExpression(expressionStack, str, new OperatorExpressionMod());
|
||||||
break;
|
break;
|
||||||
case Token.Type_OperatorAdd:
|
case Token.Type_OperatorAdd:
|
||||||
PushOperatorExpression(expressionStack, tok, new OperatorExpressionAdd());
|
PushOperatorExpression(expressionStack, str, new OperatorExpressionAdd());
|
||||||
break;
|
break;
|
||||||
case Token.Type_OperatorSub:
|
case Token.Type_OperatorSub:
|
||||||
PushOperatorExpression(expressionStack, tok, new OperatorExpressionSub());
|
PushOperatorExpression(expressionStack, str, new OperatorExpressionSub());
|
||||||
break;
|
break;
|
||||||
case Token.Type_BracketClose:
|
case Token.Type_BracketClose:
|
||||||
ThrowError("unexpected '('");
|
ThrowError("unexpected '('");
|
||||||
@ -120,19 +111,56 @@ class Parser {
|
|||||||
ThrowError("unexpected ')'");
|
ThrowError("unexpected ')'");
|
||||||
break;
|
break;
|
||||||
case Token.Type_Literal:
|
case Token.Type_Literal:
|
||||||
ThrowError($"token '{tok.GetStr()}' isn't implemented");
|
switch(str){
|
||||||
|
case "sin":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionSin());
|
||||||
break;
|
break;
|
||||||
default:
|
case "cos":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionCos());
|
||||||
|
break;
|
||||||
|
case "tan":
|
||||||
|
case "tg":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionTg());
|
||||||
|
break;
|
||||||
|
case "ctg":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionCtg());
|
||||||
|
break;
|
||||||
|
case "asin":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionAsin());
|
||||||
|
break;
|
||||||
|
case "acos":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionAcos());
|
||||||
|
break;
|
||||||
|
case "atan":
|
||||||
|
case "atg":
|
||||||
|
case "arctg":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionAtg());
|
||||||
|
break;
|
||||||
|
case "actg":
|
||||||
|
case "arcctg":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionActg());
|
||||||
|
break;
|
||||||
|
case "ln":
|
||||||
|
PushFunctionExpression(expressionStack, str, new FunctionCallExpressionLn());
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
ThrowError($"invalid literal '{str}'");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
ThrowError($"unexpected token type '{type}'");
|
ThrowError($"unexpected token type '{type}'");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenNode = tokenNode.GetNext();
|
tokenNode = tokenNode.GetNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(expressionStack.Count == 1)
|
if(expressionStack.Count != 1)
|
||||||
|
ThrowError("");
|
||||||
RootExpression = expressionStack.Pop();
|
RootExpression = expressionStack.Pop();
|
||||||
else RootExpression = new NumericExpression(); // NaN
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns the number or NaN
|
// returns the number or NaN
|
||||||
@ -154,9 +182,9 @@ class Parser {
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushOperatorExpression(Stack<IExpression#>! expressionStack, Token tok, OperatorExpression# opExpr) {
|
void PushOperatorExpression(Stack<IExpression#>! expressionStack, string tokStr, OperatorExpression# opExpr) {
|
||||||
if(expressionStack.Count < 2){
|
if(expressionStack.Count < 2){
|
||||||
ThrowError($"unexpected operator '{tok.GetStr()}'");
|
ThrowError($"unexpected operator '{tokStr}'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IExpression b = expressionStack.Pop();
|
IExpression b = expressionStack.Pop();
|
||||||
@ -164,6 +192,15 @@ class Parser {
|
|||||||
opExpr.Init(a, b);
|
opExpr.Init(a, b);
|
||||||
expressionStack.Push(opExpr);
|
expressionStack.Push(opExpr);
|
||||||
}
|
}
|
||||||
|
void PushFunctionExpression(Stack<IExpression#>! expressionStack, string tokStr, FunctionCallExpression# fExpr) {
|
||||||
|
if(expressionStack.Count < 1){
|
||||||
|
ThrowError($"unexpected function call '{tokStr}'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IExpression x = expressionStack.Pop();
|
||||||
|
fExpr.Init(x);
|
||||||
|
expressionStack.Push(fExpr);
|
||||||
|
}
|
||||||
|
|
||||||
static void ThrowError(string errmsg){
|
static void ThrowError(string errmsg){
|
||||||
#if C
|
#if C
|
||||||
|
|||||||
@ -6,16 +6,16 @@ class Token {
|
|||||||
// The Type is also the priority of the token in calculation (see Parser).
|
// The Type is also the priority of the token in calculation (see Parser).
|
||||||
int Type;
|
int Type;
|
||||||
|
|
||||||
|
public const int Type_Literal=11;
|
||||||
public const int Type_OperatorPow=10;
|
public const int Type_OperatorPow=10;
|
||||||
public const int Type_OperatorMul=9;
|
public const int Type_OperatorMul=9;
|
||||||
public const int Type_OperatorMod=8;
|
public const int Type_OperatorMod=8;
|
||||||
public const int Type_OperatorDiv=7;
|
public const int Type_OperatorDiv=7;
|
||||||
public const int Type_OperatorAdd=6;
|
public const int Type_OperatorAdd=6;
|
||||||
public const int Type_OperatorSub=5;
|
public const int Type_OperatorSub=5;
|
||||||
public const int Type_BracketOpen=4;
|
public const int Type_BracketOpen=3;
|
||||||
public const int Type_BracketClose=3;
|
public const int Type_BracketClose=2;
|
||||||
public const int Type_Number=2;
|
public const int Type_Number=1;
|
||||||
public const int Type_Literal=1;
|
|
||||||
|
|
||||||
internal static Token() Create(string str, int startIndex, int length, int type){
|
internal static Token() Create(string str, int startIndex, int length, int type){
|
||||||
Token() tok = {
|
Token() tok = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user