amath/app/main/parser.cpp

657 lines
16 KiB
C++

/*
* Copyright (c) 2015-2017 Carsten Sonne Larsen <cs@innolan.dk>
* 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.
*
*/
#include "clib.h"
#include "lib/numb.h"
#include "lib/real.h"
#include "lib/cplex.h"
#include "main/parser.h"
#include "main/values.h"
#include "main/operators.h"
#include "main/functions.h"
#include "main/statements.h"
#include "localize/text.h"
#include "system/program.h"
Parser::Parser(const char *input)
{
lexer = new Lexer(input);
}
Parser::~Parser()
{
delete lexer;
}
SyntaxNode* Parser::Parse()
{
lexer->Tokenize();
token = NOMEM;
StatementBlockNode *block = NOMEM;
SyntaxNode *result;
Token *current;
do {
result = TryParseStatement();
GetToken();
if (token->symbol == symdelimiter || (token->symbol == symend && block != NOMEM)) {
if (block == NOMEM) {
block = new StatementBlockNode();
}
if (result != NOMEM) {
block->Add(result);
}
while (token->symbol == symdelimiter) {
GetToken();
}
} else if (token->symbol != symend) {
if (result != NOMEM) {
delete result;
}
result = new ErrorNode(lexer->GetInput(), token->GetPos());
if (block != NOMEM) {
block->Add(result);
}
GetToken();
}
current = token;
PutToken();
} while (current->symbol != symend);
return block != NOMEM ? block :
result != NOMEM ? result : new EmptyStatement();;
}
SyntaxNode* Parser::TryParseStatement()
{
GetToken();
if(token->symbol == symend || token->symbol == symdelimiter) {
PutToken();
return NOMEM;
}
PutToken();
errorNode = NOMEM;
syntaxError = -1;
SyntaxNode *result = ParseStatement();
if (errorNode == NOMEM && syntaxError != -1) {
errorNode = new ErrorNode(lexer->GetInput(), syntaxError);
}
if (errorNode != NOMEM) {
delete result;
result = errorNode;
}
return result;
}
// -----------------------------------------------------
// -------------- Recursive-descent logic --------------
// -----------------------------------------------------
SyntaxNode* Parser::ParseStatement()
{
SyntaxNode* res = NOMEM;
GetToken();
switch (token->symbol) {
case symhelp:
res = ParseHelpStatement();
break;
case symdelete:
res = ParseDeleteStatement();
break;
case symdef:
res = ParseFunctionDef();
break;
case symlist:
res = ParseListStatement();
break;
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 symversion:
res = new VersionStatement();
break;
case symexit:
res = new ExitStatement();
break;
case symclear:
res = new ClearStatement();
break;
case symmem:
res = new MemoryStatement();
break;
case symvariable:
res = new ListVariablesStatement();
break;
case symfunction:
res = new ListFunctionsStatement();
break;
case symeval:
res = new EvalStatement(ParseExpression());
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 = NOMEM;
token = temp;
if (funcdef) {
return ParseFunctionDef();
} else {
return ParseEvaluation();
}
}
SyntaxNode* Parser::ParseEvaluation()
{
ExpressionNode *exp = ParseExpression();
if (exp == NOMEM) {
return NOMEM;
}
StatementNode *sta = new EvalStatement(exp);
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();
}
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 == NOMEM) {
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 == NOMEM) {
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 = NOMEM;
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 ()
{
token = (token == NOMEM ? lexer->GetFirstToken() : token->GetNextToken());
}
void Parser::PutToken()
{
token = token->GetLastToken();
}
bool Parser::Expect(Symbol symbol)
{
GetToken();
if (token->symbol != symbol) {
syntaxError = token->GetPos();
return false;
} else {
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 NOMEM;
}
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();
} else if (token->symbol == symident) {
return new HelpStatement(token->GetText());
} else {
return new HelpStatement(token->symbol);
}
}
SyntaxNode* Parser::ParseDeleteStatement()
{
GetToken();
if (token->symbol == symvariable || token->symbol == symfunction) {
return new DeleteStatement(token->symbol);
} else if (token->symbol != symident) {
syntaxError = token->GetPos();
return NOMEM;
}
Token *identToken = token;
GetToken();
if (token->symbol == symlparen) {
Expect(symident);
Token *parameter = token;
Expect(symrparen);
return new DeleteStatement(identToken->GetText(), parameter->GetText());
} else {
PutToken();
return new DeleteStatement(token->GetText());
}
}
SyntaxNode* Parser::ParseListStatement()
{
GetToken();
if (token->symbol == symqident)
return new ListStatement(token->GetText());
else if (token->symbol == symend || symdelimiter) {
PutToken();
return new ListStatement();
} else {
syntaxError = token->GetPos();
return NOMEM;
}
}
SyntaxNode* Parser::ParseFileStatement()
{
Token *statement = token;
Expect(symqident);
Token *identToken = token;
if (statement->symbol == symload) {
return new LoadStatement(identToken->GetText());
} else if (statement->symbol == symsave) {
return new SaveStatement(identToken->GetText());
} else if (statement->symbol == symexecute) {
return new ExecuteStatement(identToken->GetText());
} else if (statement->symbol == symshow) {
return new ShowStatement(identToken->GetText());
} else {
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) ?
(SyntaxNode*)new InputStatement() :
(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 NOMEM;
}
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 NOMEM;
}
}
return (statement->symbol == syminput) ?
(SyntaxNode*)new InputStatement(base) :
(SyntaxNode*)new OutputStatement(base);
}
SyntaxNode* Parser::ParseDigistStatement()
{
GetToken();
if (token->symbol == symdelimiter || token->symbol == symend) {
PutToken();
return new DigitsStatement();
} else if (token->symbol != symnumber) {
syntaxError = token->GetPos();
return NOMEM;
}
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 NOMEM;
}
return new DigitsStatement(digits);
}
SyntaxNode* Parser::ParsePromptStatement()
{
GetToken();
if (token->symbol == symqident) {
return new PromptStatement(token->GetText());
}
PutToken();
return NOMEM;
}
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());
} else {
return new DrawStatement(identToken->GetText(), paramToken->GetText());
}
}