diff --git a/build.sh b/build.sh index 8fd723c..d0ae125 100644 --- a/build.sh +++ b/build.sh @@ -3,14 +3,15 @@ set -eo pipefail OUT_FILE="out/FusionCalculator.c" BIN_FILE="bin/FusionCalculator.exe" +SRC_FILES="$(find src/ -name '*.fu')" WARNINGS="-Wall -Wno-unused-value -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers" INCLUDES="$(pkg-config --cflags glib-2.0)" COMPILER_ARGS="-O0 -g" -LINKER_ARGS="$(pkg-config --libs glib-2.0)" +LINKER_ARGS="$(pkg-config --libs glib-2.0) -lm" rm -rf out bin mkdir out bin -fut -l c -o "$OUT_FILE" src/*.fu +fut -l c -D C -o "$OUT_FILE" $SRC_FILES gcc $WARNINGS $COMPILER_ARGS "$OUT_FILE" -o "$BIN_FILE" $INCLUDES $LINKER_ARGS diff --git a/src/Calculator.fu b/src/Calculator.fu index 0b1ec76..da81c4d 100644 --- a/src/Calculator.fu +++ b/src/Calculator.fu @@ -6,21 +6,17 @@ public static class Calculator joined += arg; } double rezult = Calculate(joined); - // Console.WriteLine(rezult); + Console.WriteLine(rezult); } public static double Calculate(string exprStr) { Lexer() lexer = { ExprStr = exprStr}; List tokens = lexer.Lex(); - for(int i = 0; i < tokens.Count; i++){ - Token() tok = tokens[i]; - int tokType = tok.GetType(); - string() tokStr = tok.GetStr(); - Console.WriteLine($"'{tokStr}' {tokType:D1}"); - } + // lexer.PrintTokens(); Parser() parser = { TokenStorage = tokens }; IExpression expr = parser.Parse(); + // parser.PrintGraphVizCode(); return expr.Calculate(); } } diff --git a/src/Expressions.fu b/src/Expressions.fu deleted file mode 100644 index 35401fa..0000000 --- a/src/Expressions.fu +++ /dev/null @@ -1,29 +0,0 @@ -abstract class IExpression -{ - internal abstract double Calculate(); -} - -class NumericExpression : IExpression -{ - double N; - internal override double Calculate() => N; -} - -abstract class OperatorExpression : IExpression -{ - IExpression a; - IExpression b; - - internal abstract double OperatorImplementation(double a, double b); - - internal override double Calculate() => OperatorImplementation(a.Calculate(), b.Calculate()); -} - -abstract class FunctionCallExpression : IExpression -{ - IExpression x; - - internal abstract double FunctionImplementation(double x); - - internal override double Calculate() => FunctionImplementation(x.Calculate()); -} diff --git a/src/Expressions/FunctionCallExpression.fu b/src/Expressions/FunctionCallExpression.fu new file mode 100644 index 0000000..d001afd --- /dev/null +++ b/src/Expressions/FunctionCallExpression.fu @@ -0,0 +1,36 @@ +abstract class FunctionCallExpression : IExpression +{ + IExpression X; + + internal void Init!(IExpression x){ + X = x; + } + + internal abstract double FunctionImplementation(double x); + + internal override double Calculate() => FunctionImplementation(X.Calculate()); +} + +class FunctionCallExpressionSin : FunctionCallExpression { + internal override double FunctionImplementation(double x) => Math.Sin(x); +} +class FunctionCallExpressionCos : FunctionCallExpression { + internal override double FunctionImplementation(double x) => Math.Cos(x); +} +class FunctionCallExpressionTan : FunctionCallExpression { + internal override double FunctionImplementation(double x) => Math.Tan(x); +} + +class FunctionCallExpressionAsin : FunctionCallExpression { + internal override double FunctionImplementation(double x) => Math.Asin(x); +} +class FunctionCallExpressionAcos : FunctionCallExpression { + internal override double FunctionImplementation(double x) => Math.Acos(x); +} +class FunctionCallExpressionAtan : FunctionCallExpression { + internal override double FunctionImplementation(double x) => Math.Atan(x); +} + +class FunctionCallExpressionLog : FunctionCallExpression{ + internal override double FunctionImplementation(double x) => Math.Log(x); +} diff --git a/src/Expressions/IExpression.fu b/src/Expressions/IExpression.fu new file mode 100644 index 0000000..c2ffbd9 --- /dev/null +++ b/src/Expressions/IExpression.fu @@ -0,0 +1,4 @@ +abstract class IExpression +{ + internal abstract double Calculate(); +} diff --git a/src/Expressions/NumericalExpression.fu b/src/Expressions/NumericalExpression.fu new file mode 100644 index 0000000..13268b7 --- /dev/null +++ b/src/Expressions/NumericalExpression.fu @@ -0,0 +1,5 @@ +class NumericExpression : IExpression +{ + double N; + internal override double Calculate() => N; +} diff --git a/src/Expressions/OperatorExpression.fu b/src/Expressions/OperatorExpression.fu new file mode 100644 index 0000000..926df26 --- /dev/null +++ b/src/Expressions/OperatorExpression.fu @@ -0,0 +1,51 @@ +abstract class OperatorExpression : IExpression +{ + IExpression A; + IExpression B; + + internal void Init!(IExpression a, IExpression b) { + A = a; + B = b; + } + + internal abstract double OperatorImplementation(double a, double b); + + internal override double Calculate() => OperatorImplementation(A.Calculate(), B.Calculate()); +} + +class OperatorExpressionAdd : OperatorExpression { + internal override double OperatorImplementation(double a, double b) => a+b; +} + +class OperatorExpressionSub : OperatorExpression { + internal override double OperatorImplementation(double a, double b) => a-b; +} + +class OperatorExpressionMul : OperatorExpression { + internal override double OperatorImplementation(double a, double b) => a*b; +} + +class OperatorExpressionDiv : OperatorExpression { + internal override double OperatorImplementation(double a, double b) => a/b; +} + +class OperatorExpressionMod : OperatorExpression { + // returns if b>0 then returns a%b else returns a + internal override double OperatorImplementation(double a, double b) { + if(b <= 0) + return a; + if(a > 0){ + while(a >= b) + a -= b; + } + else { + while(a <= b) + a += b; + } + return a; + } +} + +class OperatorExpressionPow : OperatorExpression { + internal override double OperatorImplementation(double a, double b) => Math.Pow(a, b); +} diff --git a/src/Lexer.fu b/src/Lexer.fu index 1316a14..cb72d2e 100644 --- a/src/Lexer.fu +++ b/src/Lexer.fu @@ -27,6 +27,15 @@ class Lexer TokSub = Token.Create("-", 0, 1, Token.Type_OperatorSub); } + internal void PrintTokens(){ + for(int i = 0; i < TokenStorage.Count; i++){ + Token() tok = TokenStorage[i]; + int tokType = tok.GetType(); + string() tokStr = tok.GetStr(); + Console.WriteLine($"'{tokStr}' {tokType:D1}"); + } + } + internal List Lex!() { while (i < ExprStr.Length) diff --git a/src/Parser.fu b/src/Parser.fu index 2d2110f..1be71bc 100644 --- a/src/Parser.fu +++ b/src/Parser.fu @@ -1,18 +1,27 @@ class Parser { List TokenStorage; - Stack() RPNStack; - IExpression# RootNode; + IExpression# RootExpression; TokenLinkedList() TokensInRPN; + internal Parser(){ + RootExpression = new NumericExpression { N = Math.NaN }; + } + internal IExpression# Parse!(){ SortTokensInRPN(); - BuildTreeFromRPN(); - return RootNode; + BuildExpressionTree(); + return RootExpression; + } + + internal void PrintGraphVizCode(){ + string() graphviz = TokensInRPN.ToGraphVizCode(); + Console.WriteLine(graphviz); } // Implementation of https://en.wikipedia.org/wiki/Shunting_yard_algorithm void SortTokensInRPN!(){ + Stack() RPNStack; for(int i = 0; i < TokenStorage.Count; i++){ Token tok = TokenStorage[i]; int type = tok.GetType(); @@ -38,15 +47,18 @@ class Parser case Token.Type_BracketClose: while(RPNStack.Count != 0){ Token op2 = RPNStack.Pop(); + if(op2.GetType() == Token.Type_BracketOpen){ + break; + } TokensInRPN.AddToEnd(op2); } break; case Token.Type_Literal: - // not implemented - assert false; + ThrowError("not implemented"); + break; default: - // unexpected token type - assert false; + ThrowError("unexpected token type"); + break; } } @@ -57,9 +69,89 @@ class Parser } } - void BuildTreeFromRPN!(){ - RootNode = new NumericExpression { N = 0 }; - string() graphviz = TokensInRPN.ToGraphVizCode(); - Console.WriteLine(graphviz); + // Implementation of https://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%80%D0%B0%D1%82%D0%BD%D0%B0%D1%8F_%D0%BF%D0%BE%D0%BB%D1%8C%D1%81%D0%BA%D0%B0%D1%8F_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D1%8C#%D0%9E%D0%B1%D1%89%D0%B8%D0%B9_%D0%BF%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D0%BA + void BuildExpressionTree!(){ + Stack() expressionStack; + + TokenLinkedListNode? tokenNode = TokensInRPN.GetFirstNode(); + while(tokenNode != null){ + Token tok = tokenNode.GetValue(); + + switch(tok.GetType()){ + case Token.Type_Number: + expressionStack.Push(new NumericExpression { N = StringToDouble(tok.GetStr()) }); + break; + case Token.Type_OperatorPow: + PushOperatorExpression(expressionStack, tok, new OperatorExpressionPow()); + break; + case Token.Type_OperatorMul: + PushOperatorExpression(expressionStack, tok, new OperatorExpressionMul()); + break; + case Token.Type_OperatorDiv: + PushOperatorExpression(expressionStack, tok, new OperatorExpressionDiv()); + break; + case Token.Type_OperatorMod: + PushOperatorExpression(expressionStack, tok, new OperatorExpressionMod()); + break; + case Token.Type_OperatorAdd: + PushOperatorExpression(expressionStack, tok, new OperatorExpressionAdd()); + break; + case Token.Type_OperatorSub: + PushOperatorExpression(expressionStack, tok, new OperatorExpressionSub()); + break; + case Token.Type_BracketClose: + case Token.Type_BracketOpen: + ThrowError("brackets should be handled at SortTokensInRPN"); + break; + case Token.Type_Literal: + ThrowError("not implemented"); + break; + default: + ThrowError("unexpected token type"); + break; + } + + tokenNode = tokenNode.GetNext(); + } + + if(expressionStack.Count == 1) + RootExpression = expressionStack.Pop(); + } + + // returns the number or NaN + static double StringToDouble(string s){ + double d = Math.NaN; + #if C + native{ + d = strtod(s, NULL); + } + #elif CS + native { + d = Convert.ToDouble(s, System.Globalization.CultureInfo.InvariantCulture); + } + #else + native { + #error "UNKNOWN PROGRAMMING LANGUAGE (define C or CS)"; + } + #endif + return d; + } + + void PushOperatorExpression(Stack! expressionStack, Token tok, OperatorExpression# opExpr) { + if(expressionStack.Count < 2){ + ThrowError("input error: unexpected operator "+tok.GetStr()); + return; + } + IExpression b = expressionStack.Pop(); + IExpression a = expressionStack.Pop(); + opExpr.Init(a, b); + expressionStack.Push(opExpr); + } + + static void ThrowError(string errmsg){ + #if C + Console.WriteLine("ERROR: "+errmsg); + #endif + assert false, errmsg; } } diff --git a/src/Token.fu b/src/Token.fu index 920554a..8216a09 100644 --- a/src/Token.fu +++ b/src/Token.fu @@ -7,14 +7,14 @@ class Token // The Type is also the priority of the token in calculation (see Parser). int Type; - public const int Type_BracketOpen=10; - public const int Type_BracketClose=9; - public const int Type_OperatorPow=8; - public const int Type_OperatorMul=7; - public const int Type_OperatorMod=6; - public const int Type_OperatorDiv=5; - public const int Type_OperatorAdd=4; - public const int Type_OperatorSub=3; + public const int Type_OperatorPow=10; + public const int Type_OperatorMul=9; + public const int Type_OperatorMod=8; + public const int Type_OperatorDiv=7; + public const int Type_OperatorAdd=6; + public const int Type_OperatorSub=5; + public const int Type_BracketOpen=4; + public const int Type_BracketClose=3; public const int Type_Number=2; public const int Type_Literal=1; diff --git a/src/TokenLinkedList.fu b/src/TokenLinkedList.fu index 98f37a1..aff255f 100644 --- a/src/TokenLinkedList.fu +++ b/src/TokenLinkedList.fu @@ -57,6 +57,9 @@ class TokenLinkedList { // enumerating the tree from FirstNode while(nextNode != null){ string() nextTokStr = nextNode.GetValue().GetStr(); + // % is an escape character in GraphViz + if(nextTokStr[0]=='%') + nextTokStr = "\\" + nextTokStr; string() nextNodeName = "\""; nextNodeName += nextTokStr; nextNodeName += "\"";