amath/src/main/parser.cpp

789 lines
17 KiB
C++

/*-
* Copyright (c) 2014-2021 Carsten Sonne Larsen <cs@innolan.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Project homepage:
* https://amath.innolan.net
*
*/
#include "amath.h"
#include "amathc.h"
#include "parser.h"
#include "values.h"
#include "operators.h"
#include "statements.h"
#include "userfunction.h"
#include "lib/numb.h"
#include "lib/cplex.h"
#include "lib/ntextd.h"
#include "loc/text.h"
#include "system/program.h"
Parser::Parser(const char* input)
{
lexer = new Lexer(input);
token = nullptr;
errorNode = nullptr;
syntaxError = 0;
ResultOnly = false;
}
Parser::~Parser()
{
delete lexer;
}
SyntaxNode* Parser::Parse()
{
lexer->Tokenize();
token = nullptr;
StatementBlockNode* block = nullptr;
SyntaxNode* result;
Token* current;
do
{
result = TryParseStatement();
GetToken();
if (token->symbol == symdelimiter || (token->symbol == symend && block != nullptr))
{
if (block == nullptr)
{
block = new StatementBlockNode();
}
if (result != nullptr)
{
block->Add(result);
}
while (token->symbol == symdelimiter)
{
GetToken();
}
}
else if (token->symbol != symend)
{
if (result != nullptr)
{
delete result;
}
if (block == nullptr)
{
block = new StatementBlockNode();
}
result = new ErrorNode(lexer->GetInput(), token->GetPos());
block->Add(result);
// Skip until next statement
do
{
GetToken();
}
while (token->symbol != symdelimiter && token->symbol != symend);
}
current = token;
PutToken();
}
while (current->symbol != symend);
if (block != nullptr)
return block;
if (result == nullptr)
return new EmptyStatement();
return result;;
}
SyntaxNode* Parser::TryParseStatement()
{
GetToken();
if (token->symbol == symend || token->symbol == symdelimiter)
{
PutToken();
return nullptr;
}
PutToken();
errorNode = nullptr;
syntaxError = -1;
SyntaxNode* result = ParseStatement();
if (errorNode == nullptr && syntaxError != -1)
{
errorNode = new ErrorNode(lexer->GetInput(), syntaxError);
}
if (errorNode != nullptr)
{
delete result;
result = errorNode;
}
return result;
}
// -----------------------------------------------------
// -------------- Recursive-descent logic --------------
// -----------------------------------------------------
SyntaxNode* Parser::ParseStatement()
{
SyntaxNode* res;
GetToken();
switch (token->symbol)
{
case symhelp:
res = ParseHelpStatement();
break;
case symdelete:
res = ParseDeleteStatement();
break;
case symdef:
res = ParseFunctionDef();
break;
#if defined(UNIX) || defined(HAIKU) || defined(AMIGA)
case symlist:
res = ParseListStatement();
break;
#endif
case symshow:
case symload:
case symsave:
case symexecute:
res = ParseFileStatement();
break;
case syminput:
case symoutput:
res = ParseNumeralStatement();
break;
case symprompt:
res = ParsePromptStatement();
break;
case symprefs:
res = ParsePrefsStatement();
break;
case symdigits:
res = ParseDigistStatement();
break;
case symdraw:
case symplot:
res = ParseDrawStatement();
break;
case symabout:
res = new AboutStatement();
break;
case symlicense:
res = new LicenseStatement();
break;
case symversion:
res = new VersionStatement();
break;
case symexit:
res = new ExitStatement();
break;
#if defined(AMIGA) || defined(TERMIOS) || defined(WINDOWS)
case symclear:
res = new ClearStatement();
break;
#endif
case symmem:
res = new MemoryStatement();
break;
case symvariable:
res = new ListVariablesStatement();
break;
case symfunction:
res = new ListFunctionsStatement();
break;
case symeval:
res = new EvalStatement(ParseExpression(), ResultOnly);
break;
default:
PutToken();
res = ParseDefault();
}
return res;
}
SyntaxNode* Parser::ParseDefault()
{
Token* temp = token;
// Peek tokens
bool funcdef = Expect(symident);
funcdef = funcdef && Expect(symlparen);
funcdef = funcdef && Expect(symident);
funcdef = funcdef && Expect(symrparen);
funcdef = funcdef && Expect(symassign);
// Restart parsing
syntaxError = -1;
errorNode = nullptr;
token = temp;
if (funcdef)
{
return ParseFunctionDef();
}
return ParseEvaluation();
}
SyntaxNode* Parser::ParseEvaluation()
{
ExpressionNode* exp = ParseExpression();
if (exp == nullptr)
{
return nullptr;
}
StatementNode* sta = new EvalStatement(exp, ResultOnly);
if (exp->IsSilent())
{
sta = new SilentStatement(sta);
}
return sta;
}
ExpressionNode* Parser::ParseExpression()
{
return ParseAddSubstract();
}
ExpressionNode* Parser::ParseAddSubstract()
{
ExpressionNode* node = ParseFactor();
GetToken();
while (token->symbol == symplus || token->symbol == symminus)
{
if (token->symbol == symplus)
{
node = new AdditionNode(node, ParseFactor());
}
else if (token->symbol == symminus)
{
node = new SubtractionNode(node, ParseFactor());
}
GetToken();
}
PutToken();
return node;
}
ExpressionNode* Parser::ParseFactor()
{
ExpressionNode* node = ParsePower();
GetToken();
while (token->symbol == symtimes || token->symbol == symslash)
{
if (token->symbol == symtimes)
{
node = new MultiplicationNode(node, ParsePower());
}
else if (token->symbol == symslash)
{
node = new DivisionNode(node, ParsePower());
}
GetToken();
}
PutToken();
return node;
}
ExpressionNode* Parser::ParsePower()
{
ExpressionNode* node = ParseUnary();
GetToken();
while (token->symbol == sympower)
{
node = new PowerNode(node, ParseUnary());
GetToken();
}
PutToken();
return node;
}
ExpressionNode* Parser::ParseUnary()
{
ExpressionNode* node;
GetToken();
if (token->symbol == symminus)
{
node = new UnaryNode(ParseAtomic());
}
else
{
PutToken();
node = ParseAtomic();
}
return node;
}
ExpressionNode* Parser::ParseAtomic()
{
ExpressionNode* node;
GetToken();
if (token->symbol == symlparen)
{
node = ParseExpression();
Expect(symrparen);
}
else if (token->symbol == symabsolute)
{
node = new AbsoluteNode(ParseExpression());
Expect(symabsolute);
}
else if (token->symbol == symident)
{
node = ParseIdent();
}
else if (token->symbol == sympi)
{
node = new PiNode();
}
else if (token->symbol == syme)
{
node = new EulersNumberNode();
}
else if (token->symbol == symi)
{
node = new ComplexiNode();
}
else if (token->symbol == symins)
{
node = new InsVariableNode();
}
else if (token->symbol == symnumber)
{
node = ParseNumber();
}
else
{
node = new NumericValueNode();
syntaxError = token->GetPos();
}
if (Peek()->symbol == symfactorial)
{
GetToken();
node = new FactorialNode(node);
}
return node;
}
ExpressionNode* Parser::ParseIdent()
{
ExpressionNode* node;
Token* identToken = token;
GetToken();
if (token->symbol == symlparen)
{
ExpressionNode* parameter = ParseExpression();
Expect(symrparen);
node = Program->Functions->GetFunctionCall(identToken->GetText(), parameter);
if (node == nullptr)
{
errorNode = new ErrorNode(
lexer->GetInput(),
HELPFUNNDEF,
identToken->GetText(),
identToken->GetPos());
delete parameter;
node = new NumericValueNode();
}
}
else if (token->symbol == symassign)
{
Variable* var = Program->Variables->CreateVariable(identToken->GetText());
node = new AssignmentNode(new VariableNode(var), ParseExpression());
}
else
{
PutToken();
Variable* var = Program->Variables->GetVariable(token->GetText());
if (var == nullptr)
{
errorNode = new ErrorNode(
lexer->GetInput(),
HELPVARNDEF,
identToken->GetText(),
identToken->GetPos());
node = new NumericValueNode();
}
else
{
node = new VariableNode(var);
}
}
return node;
}
ExpressionNode* Parser::ParseNumber()
{
const char* a = token->GetText();
Number* number;
GetToken();
if (token->symbol == symi)
{
Number* imag = Program->Input->Parse(a);
number = new ComplexNumber(0.0, imag->GetRealValue());
delete imag;
}
else
{
PutToken();
number = Program->Input->Parse(a);
}
return new NumericValueNode(number);
}
// -----------------------------------------------------
// ------------------- Token logic ---------------------
// -----------------------------------------------------
void Parser::GetToken()
{
if (token == nullptr)
{
token = lexer->GetFirstToken();
}
else
{
token = token->GetNextToken();
}
}
Token* Parser::Peek() const
{
if (token == nullptr)
return lexer->GetFirstToken();
return token->GetNextToken();
}
void Parser::PutToken()
{
token = token->GetLastToken();
}
bool Parser::Expect(Symbol symbol)
{
GetToken();
if (token->symbol != symbol)
{
syntaxError = token->GetPos();
return false;
}
return true;
}
// -----------------------------------------------------
// ----------------- Statement logic -------------------
// -----------------------------------------------------
SyntaxNode* Parser::ParseFunctionDef()
{
Expect(symident);
Token* funcToken = token;
Expect(symlparen);
Expect(symident);
Token* funcVariable = token;
Expect(symrparen);
Expect(symassign);
if (Program->Functions->IsSystemFunction(funcToken->GetText()))
{
errorNode = new ErrorNode(
lexer->GetInput(),
HELPFUNRDEF,
funcToken->GetText(),
funcToken->GetPos());
return nullptr;
}
UserFunction* function = Program->Functions->GetFunctionDef(funcToken->GetText());
Variable* variable = function->CreateVariable(funcVariable->GetText());
Program->Variables->InsertTemporaryVariable(variable);
function->SetExpression(ParseExpression());
Program->Variables->RemoveTemporaryVariable();
// TODO: Move logic to FunctionDefinitionNode
return new FunctionDefinitionNode();
}
SyntaxNode* Parser::ParseHelpStatement()
{
GetToken();
if (token->symbol == symdelimiter || token->symbol == symend)
{
PutToken();
return new HelpStatement();
}
if (token->symbol == symident)
return new HelpStatement(token->GetText());
return new HelpStatement(token->symbol);
}
SyntaxNode* Parser::ParseDeleteStatement()
{
GetToken();
if (token->symbol == symvariable || token->symbol == symfunction)
{
return new DeleteStatement(token->symbol);
}
if (token->symbol != symident)
{
syntaxError = token->GetPos();
return nullptr;
}
Token* identToken = token;
GetToken();
if (token->symbol == symlparen)
{
Expect(symident);
Token* parameter = token;
Expect(symrparen);
return new DeleteStatement(identToken->GetText(), parameter->GetText());
}
PutToken();
return new DeleteStatement(token->GetText());
}
SyntaxNode* Parser::ParseListStatement()
{
GetToken();
if (token->symbol == symqident)
return new ListStatement(token->GetText());
if (token->symbol == symend || token->symbol == symdelimiter)
{
PutToken();
return new ListStatement();
}
syntaxError = token->GetPos();
return nullptr;
}
SyntaxNode* Parser::ParseFileStatement()
{
Token* statement = token;
Expect(symqident);
Token* identToken = token;
if (statement->symbol == symload)
return new LoadStatement(identToken->GetText());
if (statement->symbol == symsave)
return new SaveStatement(identToken->GetText());
if (statement->symbol == symexecute)
return new ExecuteStatement(identToken->GetText());
if (statement->symbol == symshow)
return new ShowStatement(identToken->GetText());
return new ErrorNode(
lexer->GetInput(),
HELPUERROR, EMPTYSTRING,
statement->GetPos());
}
SyntaxNode* Parser::ParseNumeralStatement()
{
Token* statement = token;
unsigned int base;
GetToken();
switch (token->symbol)
{
case symend:
case symdelimiter:
PutToken();
return (statement->symbol == syminput)
? static_cast<SyntaxNode*>(new InputStatement())
: static_cast<SyntaxNode*>(new OutputStatement());
case symbin:
base = 2;
break;
case symoct:
base = 8;
break;
case symdec:
base = 10;
break;
case symhex:
base = 16;
break;
default:
base = 0;
}
if (base == 0 && token->symbol != symnumber)
{
syntaxError = token->GetPos();
return nullptr;
}
if (base == 0)
{
NumeralSystem* nsys = new DecimalSystem(0);
Number* number = nsys->Parse(token->GetText());
base = number->GetIntegerValue();
delete number;
delete nsys;
if (base < 2 || base > 32)
{
errorNode = new ErrorNode(
lexer->GetInput(),
HELPPNUMERA,
token->GetText(),
token->GetPos());
return nullptr;
}
}
return (statement->symbol == syminput)
? static_cast<SyntaxNode*>(new InputStatement(base))
: static_cast<SyntaxNode*>(new OutputStatement(base));
}
SyntaxNode* Parser::ParseDigistStatement()
{
GetToken();
if (token->symbol == symdelimiter || token->symbol == symend)
{
PutToken();
return new DigitsStatement();
}
if (token->symbol != symnumber)
{
syntaxError = token->GetPos();
return nullptr;
}
NumeralSystem* nsys = new DecimalSystem(0);
Number* number = nsys->Parse(token->GetText());
int digits = number->GetIntegerValue();
delete number;
delete nsys;
if (digits < 0 || digits > 15)
{
errorNode = new ErrorNode(
lexer->GetInput(),
HELPPDIGITS,
token->GetText(),
token->GetPos());
return nullptr;
}
return new DigitsStatement(digits);
}
SyntaxNode* Parser::ParsePromptStatement()
{
GetToken();
if (token->symbol == symqident)
{
return new PromptStatement(token->GetText());
}
PutToken();
return nullptr;
}
SyntaxNode* Parser::ParsePrefsStatement()
{
GetToken();
if (token->symbol == symload || token->symbol == symsave)
{
return new PrefsStatement(token->symbol);
}
PutToken();
return new PrefsStatement();;
}
SyntaxNode* Parser::ParseDrawStatement()
{
Token* statement = token;
Expect(symident);
Token* identToken = token;
Expect(symlparen);
Expect(symident);
Token* paramToken = token;
Expect(symrparen);
if (statement->symbol == symplot)
return new PlotStatement(identToken->GetText(), paramToken->GetText());
return new DrawStatement(identToken->GetText(), paramToken->GetText());
}