diff --git a/src/Calculator.fu b/src/Calculator.fu index 985ade6..0b1ec76 100644 --- a/src/Calculator.fu +++ b/src/Calculator.fu @@ -19,8 +19,8 @@ public static class Calculator string() tokStr = tok.GetStr(); Console.WriteLine($"'{tokStr}' {tokType:D1}"); } - Parser() parser = { Tokens = tokens }; - IExpression# expr = parser.Parse(); + Parser() parser = { TokenStorage = tokens }; + IExpression expr = parser.Parse(); return expr.Calculate(); } } diff --git a/src/Lexer.fu b/src/Lexer.fu index 50be0bf..1316a14 100644 --- a/src/Lexer.fu +++ b/src/Lexer.fu @@ -2,7 +2,7 @@ class Lexer { string ExprStr; - List() tokens; + List() TokenStorage; int tokType = Token.Type_Number; int tokBegin = 0; int i = 0; @@ -62,7 +62,7 @@ class Lexer // end last token TryEndToken(); - return tokens; + return TokenStorage; } void TryEndToken!() @@ -70,8 +70,8 @@ class Lexer if (tokBegin != i) { Token() tok = Token.Create(ExprStr, tokBegin, i - tokBegin, tokType); - tokens.Add(); - tokens[tokens.Count-1] = tok; + TokenStorage.Add(); + TokenStorage[TokenStorage.Count-1] = tok; tokType = Token.Type_Number; tokBegin = i; } @@ -80,8 +80,8 @@ class Lexer void AddStaticToken!(Token() tok) { TryEndToken(); - tokens.Add(); - tokens[tokens.Count-1] = tok; + TokenStorage.Add(); + TokenStorage[TokenStorage.Count-1] = tok; tokType = Token.Type_Number; tokBegin = ++i; } diff --git a/src/Parser.fu b/src/Parser.fu index b413ea2..2d2110f 100644 --- a/src/Parser.fu +++ b/src/Parser.fu @@ -1,9 +1,65 @@ class Parser { - List Tokens; + List TokenStorage; + Stack() RPNStack; + IExpression# RootNode; + TokenLinkedList() TokensInRPN; - internal IExpression# Parse(){ - NumericExpression# n = new NumericExpression { N = 1 }; - return n; + internal IExpression# Parse!(){ + SortTokensInRPN(); + BuildTreeFromRPN(); + return RootNode; + } + + // Implementation of https://en.wikipedia.org/wiki/Shunting_yard_algorithm + void SortTokensInRPN!(){ + for(int i = 0; i < TokenStorage.Count; i++){ + Token tok = TokenStorage[i]; + int type = tok.GetType(); + switch(type){ + case Token.Type_Number: + TokensInRPN.AddToEnd(tok); + break; + case Token.Type_OperatorPow: + case Token.Type_OperatorMul: + case Token.Type_OperatorDiv: + case Token.Type_OperatorMod: + case Token.Type_OperatorAdd: + case Token.Type_OperatorSub: + while(RPNStack.Count != 0 && RPNStack.Peek().GetType() >= type){ + Token op2 = RPNStack.Pop(); + TokensInRPN.AddToEnd(op2); + } + RPNStack.Push(tok); + break; + case Token.Type_BracketOpen: + RPNStack.Push(tok); + break; + case Token.Type_BracketClose: + while(RPNStack.Count != 0){ + Token op2 = RPNStack.Pop(); + TokensInRPN.AddToEnd(op2); + } + break; + case Token.Type_Literal: + // not implemented + assert false; + default: + // unexpected token type + assert false; + } + } + + // add remaining operators + while(RPNStack.Count != 0){ + Token op2 = RPNStack.Pop(); + TokensInRPN.AddToEnd(op2); + } + } + + void BuildTreeFromRPN!(){ + RootNode = new NumericExpression { N = 0 }; + string() graphviz = TokensInRPN.ToGraphVizCode(); + Console.WriteLine(graphviz); } } diff --git a/src/Token.fu b/src/Token.fu index 7c50ad8..920554a 100644 --- a/src/Token.fu +++ b/src/Token.fu @@ -7,16 +7,16 @@ class Token // The Type is also the priority of the token in calculation (see Parser). int Type; - public const int Type_BracketOpen=1; - public const int Type_BracketClose=2; - public const int Type_OperatorPow=3; - public const int Type_OperatorMul=4; - public const int Type_OperatorMod=5; - public const int Type_OperatorDiv=6; - public const int Type_OperatorAdd=7; - public const int Type_OperatorSub=8; - public const int Type_Number=9; - public const int Type_Literal=10; + 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_Number=2; + public const int Type_Literal=1; internal static Token() Create(string str, int startIndex, int length, int type){ Token() tok = { diff --git a/src/TokenLinkedList.fu b/src/TokenLinkedList.fu new file mode 100644 index 0000000..98f37a1 --- /dev/null +++ b/src/TokenLinkedList.fu @@ -0,0 +1,74 @@ +class TokenLinkedList { + TokenLinkedListNode!? FirstNode = null; + TokenLinkedListNode!? LastNode = null; + List() Storage; + + internal TokenLinkedListNode! GetFirstNode() => FirstNode; + internal TokenLinkedListNode! GetLastNode() => LastNode; + + internal void AddToStart!(Token tok) { + TokenLinkedListNode! node = CreateOrphanedNode(tok); + if(FirstNode == null){ + assert LastNode == null; + FirstNode = node; + LastNode = node; + } + else { + assert FirstNode.GetPrev() == null; + FirstNode.SetPrev(node); + FirstNode = node; + } + } + + internal void AddToEnd!(Token tok) { + TokenLinkedListNode! node = CreateOrphanedNode(tok); + if(LastNode == null){ + assert FirstNode == null; + FirstNode = node; + LastNode = node; + } + else { + assert LastNode.GetNext() == null; + LastNode.SetNext(node); + LastNode = node; + } + } + + internal TokenLinkedListNode# CreateOrphanedNode!(Token tok) { + TokenLinkedListNode# sharedPtr = new TokenLinkedListNode { + Value = tok, + Prev = null, + Next = null + }; + return sharedPtr; + } + + // Can be used by GraphViz to create node graph. + // GraphViz online: https://dreampuf.github.io/GraphvizOnline/ + internal string() ToGraphVizCode() { + string() s = "strict digraph {\n" + /* directed graph with no multi-edges */ + " start [shape=Mdiamond];\n" + /* 'start' node style */ + " end [shape=Msquare];\n" + /* 'end' node style */ + " node [style=filled,color=white];\n"; /* regular node style */ + + TokenLinkedListNode? currentNode = null; + TokenLinkedListNode? nextNode = FirstNode; + string() currentNodeName = "start"; + // enumerating the tree from FirstNode + while(nextNode != null){ + string() nextTokStr = nextNode.GetValue().GetStr(); + string() nextNodeName = "\""; + nextNodeName += nextTokStr; + nextNodeName += "\""; + s += $" {currentNodeName} -> {nextNodeName};\n"; + + currentNodeName = nextNodeName; + currentNode = nextNode; + nextNode = currentNode.GetNext(); + } + if(currentNode == LastNode) + s += $" {currentNodeName} -> end;\n"; + s += "}"; + return s; + } +} \ No newline at end of file diff --git a/src/TokenLinkedListNode.fu b/src/TokenLinkedListNode.fu new file mode 100644 index 0000000..359d2b8 --- /dev/null +++ b/src/TokenLinkedListNode.fu @@ -0,0 +1,26 @@ +class TokenLinkedListNode { + Token Value; + TokenLinkedListNode!? Prev = null; + TokenLinkedListNode!? Next = null; + + internal Token GetValue() => Value; + internal void SetValue!(Token value) { Value = value; } + + internal TokenLinkedListNode!? GetPrev() => Prev; + internal void SetPrev!(TokenLinkedListNode! prev) { Prev = prev; } + + internal TokenLinkedListNode!? GetNext() => Next; + internal void SetNext!(TokenLinkedListNode! next) { Next = next; } + + internal void InsertAfter!(TokenLinkedListNode! prev){ + Next = prev.GetNext(); + Prev = prev; + prev.SetNext(this); + } + + internal void InsertBefore!(TokenLinkedListNode! next){ + Next = next; + Prev = next.GetPrev(); + next.SetPrev(this); + } +} \ No newline at end of file