Add existing files
This commit is contained in:
parent
51b4289c74
commit
0f35a253a9
|
@ -0,0 +1,7 @@
|
||||||
|
.dub
|
||||||
|
docs.json
|
||||||
|
__dummy.html
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
snol
|
||||||
|
.vscode
|
|
@ -0,0 +1,193 @@
|
||||||
|
PROGRAM SPECIFICATIONS
|
||||||
|
The name of the custom language is Simple Number-Only Language (SNOL) SNOL is a simplified custom language that only involves integer and
|
||||||
|
real values, operations, and expressions.
|
||||||
|
|
||||||
|
The following set of specifications covers the rules that should be observed and implemented in using SNOL:
|
||||||
|
1. Formatting
|
||||||
|
• Whitespace
|
||||||
|
• Spaces are used to separate tokens (constructs) of the language but are not necessary. A command can have no space in it.
|
||||||
|
• Extra spaces are irrelevant
|
||||||
|
|
||||||
|
• SNOL is case-sensitive
|
||||||
|
2. Data type
|
||||||
|
• There is no type declaration
|
||||||
|
• The type of a variable is determined by the nature of the value it holds
|
||||||
|
• There are only two types of values: integer and floating-point
|
||||||
|
• Writing an integer literal follows the format described by this EBNF rule:
|
||||||
|
[-]digit{digit} where: digit represents a number from 0 to 9
|
||||||
|
• Writing a floating-point literal follows the format described by this EBNF rule:
|
||||||
|
[-]digit{digit}.{digit} where: digit represents a number from 0 to 9
|
||||||
|
|
||||||
|
3. Variable
|
||||||
|
• A variable is considered as a simple expression that evaluates to its corresponding value
|
||||||
|
• Naming a variable follows the format described by this EBNF rule:
|
||||||
|
letter{(letter|digit)}
|
||||||
|
where: letter represents a symbol from a to z and from A to Z
|
||||||
|
where: digit represents a number from 0 to 9
|
||||||
|
note: a keyword cannot be used as a variable name
|
||||||
|
• Variables must be defined first before they can be used
|
||||||
|
• To define a variable, its first use must be one of the following:
|
||||||
|
• as the destination variable in an assignment operation
|
||||||
|
• as the target variable in an input operation
|
||||||
|
• A variable that has already been defined to be of a certain type can be redefined to be of a different type by subjecting the variable to
|
||||||
|
either the assignment operation (as the destination variable) or the input operation (as the target variable).
|
||||||
|
Example: num = 0 Explanation: this defines the variable num to be of type integer since its value is 0 (an integer)
|
||||||
|
BEG num Explanation: if a floating-point value is given here, num will become a floating-point type variable
|
||||||
|
|
||||||
|
4. Assignment, input, and output operations
|
||||||
|
• Assignment operation:
|
||||||
|
var = expr
|
||||||
|
where: var represents the destination variable (where the value of expr will be stored)
|
||||||
|
where: expr is an arithmetic operation, a variable name, or a literal
|
||||||
|
• Input operation:
|
||||||
|
BEG var
|
||||||
|
where: var is the target variable (where the user-input value will be stored)
|
||||||
|
• The input operation is similar to C’s scanf function and is for asking user-input value
|
||||||
|
• Output operation:
|
||||||
|
PRINT out
|
||||||
|
where: out is either a variable name or a literal
|
||||||
|
• The output operation is similar to C’s printf function and is for displaying value
|
||||||
|
• Each of the assignment, input, and output operations does not evaluate to any value when executed.
|
||||||
|
5. Arithmetic operations
|
||||||
|
• Each operation is in infix notation and follows C’s precedence and associativity rules
|
||||||
|
• Operations:
|
||||||
|
|
||||||
|
where: expr1 is either a literal or a variable
|
||||||
|
where: expr2 is either a literal or a variable
|
||||||
|
For the modulo operation, only integer type is allowed for both expr1 and expr2
|
||||||
|
• Each operation is an expression that evaluates to its corresponding result
|
||||||
|
Example: the command “1 + 2” evaluates to 3
|
||||||
|
Addition
|
||||||
|
Subtraction
|
||||||
|
Multiplication
|
||||||
|
Division
|
||||||
|
Modulo
|
||||||
|
|
||||||
|
expr1 + expr2
|
||||||
|
expr1 - expr2
|
||||||
|
expr1 * expr2
|
||||||
|
expr1 / expr2
|
||||||
|
expr1 % expr2
|
||||||
|
|
||||||
|
Same as: expr1 + expr2 in C
|
||||||
|
Same as: expr1 - expr2 in C
|
||||||
|
Same as: expr1 * expr2 in C
|
||||||
|
Same as: expr1 / expr2 in C
|
||||||
|
Same as: expr1 % expr2 in C
|
||||||
|
|
||||||
|
• All operands in an operation should be of the same type
|
||||||
|
Example: the command “2 + 1.5” is not valid since the operands 2 and 1.5 are not of the same type
|
||||||
|
|
||||||
|
6. Exit operation: When given, this operation terminates the interpreter environment. The exit operation takes the form: EXIT!
|
||||||
|
7. Command: A command can be any valid literal, variable, and operation.
|
||||||
|
|
||||||
|
Sample run of the program you will be writing to implement SNOL and the corresponding explanation of each part:
|
||||||
|
Sample run:
|
||||||
|
The SNOL environment is now active, you may proceed with
|
||||||
|
giving your commands.
|
||||||
|
Command: num = 0
|
||||||
|
Command: PRINT num
|
||||||
|
SNOL> [num] = 0
|
||||||
|
Command: BEG var
|
||||||
|
SNOL> Please enter value for [var]
|
||||||
|
Input: 75
|
||||||
|
Command: 1 + var2
|
||||||
|
SNOL> Error! [var2] is not defined!
|
||||||
|
Command: num + 1.5
|
||||||
|
SNOL> Error! Operands must be of the same type in an
|
||||||
|
arithmetic operation!
|
||||||
|
Command: BEG num
|
||||||
|
SNOL> Please enter value for [num]:
|
||||||
|
Input: 25.3
|
||||||
|
Command: num + 1.5
|
||||||
|
Command: + num 6
|
||||||
|
SNOL> Unknown command! Does not match any valid
|
||||||
|
command of the language.
|
||||||
|
Command: PRINT num
|
||||||
|
SNOL> [num] = 25.3
|
||||||
|
Command: num + BEG num 3
|
||||||
|
SNOL> Unknown command! Does not match any valid
|
||||||
|
command of the language.
|
||||||
|
Command: num = 2 + 3
|
||||||
|
Command: PRINT num
|
||||||
|
SNOL> [num] = 5
|
||||||
|
Command: var
|
||||||
|
Command: EXIT!
|
||||||
|
Interpreter is now terminated...
|
||||||
|
|
||||||
|
Explanation:
|
||||||
|
Introductory message. Displayed at the start of the
|
||||||
|
|
||||||
|
|
||||||
|
interpreter run.
|
||||||
|
Assignment operation on [num]
|
||||||
|
Output operation on [num]
|
||||||
|
Input operation on [var]
|
||||||
|
Interpreter is asking for the value to be stored to [var]
|
||||||
|
trying to add 1 and the value of [var2], but [var2] is not
|
||||||
|
|
||||||
|
|
||||||
|
defined
|
||||||
|
trying to add the value of num and 1.5, but the two values
|
||||||
|
|
||||||
|
|
||||||
|
are not of the same type
|
||||||
|
Input operation on [num]. By giving it a floating-point value,
|
||||||
|
|
||||||
|
|
||||||
|
its type now becomes floating-point.
|
||||||
|
Adding the value of [num] and 1.5
|
||||||
|
trying to give a command that is not recognized by the
|
||||||
|
|
||||||
|
|
||||||
|
language.
|
||||||
|
Display value of [num]
|
||||||
|
trying to give a command that is not recognized by the
|
||||||
|
|
||||||
|
|
||||||
|
language.
|
||||||
|
Assignment operation on [num] with the value coming from
|
||||||
|
|
||||||
|
|
||||||
|
the arithmetic operation involved.
|
||||||
|
Print operation on [num]
|
||||||
|
Simply specifying a simple expression.
|
||||||
|
Invoking the exit command
|
||||||
|
Program execution is terminated
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
For the implementation:
|
||||||
|
1. In the sample run, the lines that start with the word “Command: “ are the command lines. Whenever the interpreter is in execution: the
|
||||||
|
interpreter asks the user for a command, the user gives the command, then the interpreter executes the command. This is repeated until the
|
||||||
|
command EXIT! is executed.
|
||||||
|
2. Only one command at a time is allowed to be given in the command line. When the interpreter asks for a command, the user is to type the
|
||||||
|
command then press enter.
|
||||||
|
3. In the sample run, the lines that start with “Input: “ are for when the interpreter is asking for a value to be stored to the specified variable. This
|
||||||
|
only appears when the command given is BEG. When the interpreter asks for an input, the user is to type the input then press enter.
|
||||||
|
Everything the user types before pressing the enter key should be treated as the input the user is trying to give.
|
||||||
|
4. Command execution:
|
||||||
|
• When BEG is encountered: display message similar in the sample run and ask the user for an input value and store it to the target
|
||||||
|
variable. No message display after command is successfully executed.
|
||||||
|
• When PRINT is encountered: display the value of the operand.
|
||||||
|
• When an arithmetic operation is encountered, perform the corresponding operation. No message display when command is
|
||||||
|
successfully executed.
|
||||||
|
• When the input operation is encountered, evaluate the operand and store its value to the destination variable. No message display
|
||||||
|
when command is successfully executed.
|
||||||
|
|
||||||
|
5. Checking for errors:
|
||||||
|
• Unknown word: any word that is not a keyword (e.g., BEG, EXIT!, etc.), not a valid literal, and not a valid variable name.
|
||||||
|
Sample command: !num = 0
|
||||||
|
Sample message: Unknown word [!num]
|
||||||
|
• Undefined word: a valid variable name that has not been defined (associated to a type).
|
||||||
|
Sample command: var + 2 // Assume that this is the first use of var
|
||||||
|
Sample message: Undefined variable [var]
|
||||||
|
• Unknown command: a command that does not completely follow the correct syntax specified in the language.
|
||||||
|
Sample command: BEG 5
|
||||||
|
|
||||||
|
Sample command: num + 2 5
|
||||||
|
• Incompatible types: encountered when an arithmetic operation has operands that are not of the same type
|
||||||
|
Sample command: 2 + 1.5
|
||||||
|
• Invalid number format: encountered when the user attempts to enter an input that contains symbols not related to the format of either
|
||||||
|
integer or floating-point. Since the only data types allowed are integer and floating-point, the interpreter should check if the input given
|
||||||
|
by the user follows either integer or floating-point literal format.
|
|
@ -0,0 +1,103 @@
|
||||||
|
; Configure which static analysis checks are enabled
|
||||||
|
[analysis.config.StaticAnalysisConfig]
|
||||||
|
; Check variable, class, struct, interface, union, and function names against the Phobos style guide
|
||||||
|
style_check="disabled"
|
||||||
|
; Check for array literals that cause unnecessary allocation
|
||||||
|
enum_array_literal_check="enabled"
|
||||||
|
; Check for poor exception handling practices
|
||||||
|
exception_check="enabled"
|
||||||
|
; Check for use of the deprecated 'delete' keyword
|
||||||
|
delete_check="enabled"
|
||||||
|
; Check for use of the deprecated floating point operators
|
||||||
|
float_operator_check="enabled"
|
||||||
|
; Check number literals for readability
|
||||||
|
number_style_check="enabled"
|
||||||
|
; Checks that opEquals, opCmp, toHash, and toString are either const, immutable, or inout.
|
||||||
|
object_const_check="enabled"
|
||||||
|
; Checks for .. expressions where the left side is larger than the right.
|
||||||
|
backwards_range_check="enabled"
|
||||||
|
; Checks for if statements whose 'then' block is the same as the 'else' block
|
||||||
|
if_else_same_check="enabled"
|
||||||
|
; Checks for some problems with constructors
|
||||||
|
constructor_check="enabled"
|
||||||
|
; Checks for unused variables
|
||||||
|
unused_variable_check="enabled"
|
||||||
|
; Checks for unused labels
|
||||||
|
unused_label_check="enabled"
|
||||||
|
; Checks for unused function parameters
|
||||||
|
unused_parameter_check="enabled"
|
||||||
|
; Checks for duplicate attributes
|
||||||
|
duplicate_attribute="enabled"
|
||||||
|
; Checks that opEquals and toHash are both defined or neither are defined
|
||||||
|
opequals_tohash_check="enabled"
|
||||||
|
; Checks for subtraction from .length properties
|
||||||
|
length_subtraction_check="enabled"
|
||||||
|
; Checks for methods or properties whose names conflict with built-in properties
|
||||||
|
builtin_property_names_check="enabled"
|
||||||
|
; Checks for confusing code in inline asm statements
|
||||||
|
asm_style_check="enabled"
|
||||||
|
; Checks for confusing logical operator precedence
|
||||||
|
logical_precedence_check="enabled"
|
||||||
|
; Checks for undocumented public declarations
|
||||||
|
undocumented_declaration_check="disabled"
|
||||||
|
; Checks for poor placement of function attributes
|
||||||
|
function_attribute_check="enabled"
|
||||||
|
; Checks for use of the comma operator
|
||||||
|
comma_expression_check="enabled"
|
||||||
|
; Checks for local imports that are too broad. Only accurate when checking code used with D versions older than 2.071.0
|
||||||
|
local_import_check="disabled"
|
||||||
|
; Checks for variables that could be declared immutable
|
||||||
|
could_be_immutable_check="enabled"
|
||||||
|
; Checks for redundant expressions in if statements
|
||||||
|
redundant_if_check="enabled"
|
||||||
|
; Checks for redundant parenthesis
|
||||||
|
redundant_parens_check="enabled"
|
||||||
|
; Checks for mismatched argument and parameter names
|
||||||
|
mismatched_args_check="enabled"
|
||||||
|
; Checks for labels with the same name as variables
|
||||||
|
label_var_same_name_check="enabled"
|
||||||
|
; Checks for lines longer than 120 characters
|
||||||
|
long_line_check="disabled"
|
||||||
|
; Checks for assignment to auto-ref function parameters
|
||||||
|
auto_ref_assignment_check="enabled"
|
||||||
|
; Checks for incorrect infinite range definitions
|
||||||
|
incorrect_infinite_range_check="enabled"
|
||||||
|
; Checks for asserts that are always true
|
||||||
|
useless_assert_check="enabled"
|
||||||
|
; Check for uses of the old-style alias syntax
|
||||||
|
alias_syntax_check="enabled"
|
||||||
|
; Checks for else if that should be else static if
|
||||||
|
static_if_else_check="enabled"
|
||||||
|
; Check for unclear lambda syntax
|
||||||
|
lambda_return_check="enabled"
|
||||||
|
; Check for auto function without return statement
|
||||||
|
auto_function_check="enabled"
|
||||||
|
; Check for sortedness of imports
|
||||||
|
imports_sortedness="disabled"
|
||||||
|
; Check for explicitly annotated unittests
|
||||||
|
explicitly_annotated_unittests="disabled"
|
||||||
|
; Check for properly documented public functions (Returns, Params)
|
||||||
|
properly_documented_public_functions="disabled"
|
||||||
|
; Check for useless usage of the final attribute
|
||||||
|
final_attribute_check="enabled"
|
||||||
|
; Check for virtual calls in the class constructors
|
||||||
|
vcall_in_ctor="enabled"
|
||||||
|
; Check for useless user defined initializers
|
||||||
|
useless_initializer="disabled"
|
||||||
|
; Check allman brace style
|
||||||
|
allman_braces_check="disabled"
|
||||||
|
; Check for redundant attributes
|
||||||
|
redundant_attributes_check="enabled"
|
||||||
|
; Check public declarations without a documented unittest
|
||||||
|
has_public_example="disabled"
|
||||||
|
; Check for asserts without an explanatory message
|
||||||
|
assert_without_msg="disabled"
|
||||||
|
; Check indent of if constraints
|
||||||
|
if_constraints_indent="disabled"
|
||||||
|
; Check for @trusted applied to a bigger scope than a single function
|
||||||
|
trust_too_much="enabled"
|
||||||
|
; Check for redundant storage classes on variable declarations
|
||||||
|
redundant_storage_classes="enabled"
|
||||||
|
; Check for unused function return values
|
||||||
|
unused_result="enabled"
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
import std.stdio;
|
||||||
|
import ast;
|
||||||
|
import parser;
|
||||||
|
import runtimedata;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
|
||||||
|
Parser par;
|
||||||
|
EvalReturn ret;
|
||||||
|
Expr expr;
|
||||||
|
|
||||||
|
Variable[string] varStack;
|
||||||
|
writeln("The SNOL environment is now active, you may proceed with giving your commands.");
|
||||||
|
do
|
||||||
|
{
|
||||||
|
write("Command: ");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
par = Parser(readln());
|
||||||
|
expr = par.parse();
|
||||||
|
if (expr !is null)
|
||||||
|
{
|
||||||
|
|
||||||
|
ret = expr.evaluate(varStack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(ParseException e)
|
||||||
|
{
|
||||||
|
writeln(e.msg);
|
||||||
|
}
|
||||||
|
catch(RunException e)
|
||||||
|
{
|
||||||
|
writeln(e.msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
while (ret.type != EVAL_TYPE.EXIT);
|
||||||
|
writeln("Interpreter is now terminated...");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,366 @@
|
||||||
|
module ast;
|
||||||
|
import std.stdio;
|
||||||
|
import runtimedata;
|
||||||
|
import std.string;
|
||||||
|
import std.conv;
|
||||||
|
|
||||||
|
abstract class Expr
|
||||||
|
{
|
||||||
|
|
||||||
|
Expr masterExpr = null;
|
||||||
|
|
||||||
|
EvalReturn evaluate(ref Variable[string] varStack);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class FloatExpr : Expr
|
||||||
|
{
|
||||||
|
this(float val)
|
||||||
|
{
|
||||||
|
value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
float value;
|
||||||
|
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.FLOAT, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntExpr : Expr
|
||||||
|
{
|
||||||
|
this(int val)
|
||||||
|
{
|
||||||
|
value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
int value;
|
||||||
|
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.INTEGER, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VarExpr : Expr
|
||||||
|
{
|
||||||
|
this(string var)
|
||||||
|
{
|
||||||
|
varName = var;
|
||||||
|
}
|
||||||
|
|
||||||
|
string varName;
|
||||||
|
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
const Variable* varPtr = (varName in varStack);
|
||||||
|
if (varPtr is null)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.UNDEFINED_VAR, DATA_TYPE.NONE, varName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.VAR, DATA_TYPE.VAR_POINTER, varName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpExpr : Expr
|
||||||
|
{
|
||||||
|
this(char op, bool unary = false)
|
||||||
|
{
|
||||||
|
this.op = op;
|
||||||
|
this.unary = unary;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unary;
|
||||||
|
char op;
|
||||||
|
Expr RHE;
|
||||||
|
Expr LHE;
|
||||||
|
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
EvalReturn left = void;
|
||||||
|
if (!unary)
|
||||||
|
{
|
||||||
|
left = LHE.evaluate(varStack);
|
||||||
|
}
|
||||||
|
|
||||||
|
EvalReturn right = RHE.evaluate(varStack);
|
||||||
|
if (op == '=') //ASSIGN
|
||||||
|
{
|
||||||
|
if (right.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
throw new RunException("Error! ["~right.sVar ~ "] is not defined!");
|
||||||
|
if (left.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
varStack[left.sVar] = Variable();
|
||||||
|
|
||||||
|
if (right.type == EVAL_TYPE.VAR)
|
||||||
|
{
|
||||||
|
varStack[left.sVar].dataType = varStack[right.sVar].dataType;
|
||||||
|
if (varStack[left.sVar].dataType == DATA_TYPE.INTEGER)
|
||||||
|
varStack[left.sVar].iVal = varStack[right.sVar].iVal;
|
||||||
|
else
|
||||||
|
varStack[left.sVar].fVal = varStack[right.sVar].fVal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (right.dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
varStack[left.sVar].iVal = right.iVal;
|
||||||
|
varStack[left.sVar].dataType = right.dataType;
|
||||||
|
}
|
||||||
|
else if (right.dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
varStack[left.sVar].fVal = right.fVal;
|
||||||
|
varStack[left.sVar].dataType = right.dataType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return EvalReturn(EVAL_TYPE.ASSIGN, DATA_TYPE.NONE, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (op == '-' && unary) //UNARY -
|
||||||
|
{
|
||||||
|
if (right.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
throw new RunException("Error! ["~right.sVar ~ "] is not defined!");
|
||||||
|
if (right.type == EVAL_TYPE.VAR)
|
||||||
|
{
|
||||||
|
if (varStack[right.sVar].dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.INTEGER, -varStack[right.sVar]
|
||||||
|
.iVal);
|
||||||
|
}
|
||||||
|
else if (varStack[right.sVar].dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.FLOAT, -varStack[right.sVar].fVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (right.type == EVAL_TYPE.NUMBER)
|
||||||
|
{
|
||||||
|
if (right.dataType == DATA_TYPE.INTEGER)
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.INTEGER, -right.iVal);
|
||||||
|
else if (right.dataType == DATA_TYPE.FLOAT)
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.FLOAT, -right.fVal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (op == '+' && unary) //UNARY +
|
||||||
|
{
|
||||||
|
if (right.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
throw new RunException("Error! ["~right.sVar ~ "] is not defined!");
|
||||||
|
if (right.type == EVAL_TYPE.VAR)
|
||||||
|
{
|
||||||
|
if (varStack[right.sVar].dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.INTEGER, varStack[right.sVar]
|
||||||
|
.iVal);
|
||||||
|
}
|
||||||
|
else if (varStack[right.sVar].dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.FLOAT, varStack[right.sVar].fVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (right.type == EVAL_TYPE.NUMBER)
|
||||||
|
{
|
||||||
|
if (right.dataType == DATA_TYPE.INTEGER)
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.INTEGER, right.iVal);
|
||||||
|
else if (right.dataType == DATA_TYPE.FLOAT)
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.FLOAT, right.fVal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (indexOf("*/+-%", op) > -1) // rest
|
||||||
|
{
|
||||||
|
if (right.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
throw new RunException("Error! ["~ right.sVar ~ "] is not defined!");
|
||||||
|
if (left.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
throw new RunException("Error! ["~right.sVar ~ "] is not defined!");
|
||||||
|
|
||||||
|
DATA_TYPE r;
|
||||||
|
DATA_TYPE l;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
union Data
|
||||||
|
{
|
||||||
|
int Int;
|
||||||
|
float Float;
|
||||||
|
}
|
||||||
|
Data leftData,rightData;
|
||||||
|
|
||||||
|
|
||||||
|
if (right.type == EVAL_TYPE.VAR)
|
||||||
|
{
|
||||||
|
if (varStack[right.sVar].dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
r = varStack[right.sVar].dataType;
|
||||||
|
rightData.Int = varStack[right.sVar].iVal;
|
||||||
|
}
|
||||||
|
else if (varStack[right.sVar].dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
r = varStack[right.sVar].dataType;
|
||||||
|
rightData.Float = varStack[right.sVar].fVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (right.type == EVAL_TYPE.NUMBER)
|
||||||
|
{
|
||||||
|
if (right.dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
r = right.dataType;
|
||||||
|
rightData.Int = right.iVal;
|
||||||
|
}
|
||||||
|
else if (right.dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
r = right.dataType;
|
||||||
|
rightData.Float = right.fVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left.type == EVAL_TYPE.VAR)
|
||||||
|
{
|
||||||
|
if (varStack[left.sVar].dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
l = varStack[left.sVar].dataType;
|
||||||
|
leftData.Int = varStack[left.sVar].iVal;
|
||||||
|
}
|
||||||
|
else if (varStack[left.sVar].dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
l = varStack[left.sVar].dataType;
|
||||||
|
leftData.Float = varStack[left.sVar].fVal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (left.type == EVAL_TYPE.NUMBER)
|
||||||
|
{
|
||||||
|
if (left.dataType == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
l = right.dataType;
|
||||||
|
leftData.Int = left.iVal;
|
||||||
|
}
|
||||||
|
else if (left.dataType == DATA_TYPE.FLOAT)
|
||||||
|
{
|
||||||
|
l = left.dataType;
|
||||||
|
leftData.Float = left.fVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (l != r)
|
||||||
|
throw new RunException("Error! Operands must be of the same type in an arithmetic operation!");
|
||||||
|
|
||||||
|
if (l == DATA_TYPE.INTEGER)
|
||||||
|
{
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Int + rightData.Int);
|
||||||
|
case '-':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Int - rightData.Int);
|
||||||
|
case '*':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Int * rightData.Int);
|
||||||
|
case '/':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Int / rightData.Int);
|
||||||
|
case '%':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Int % rightData.Int);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
switch (op)
|
||||||
|
{
|
||||||
|
case '+':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Float + rightData.Float);
|
||||||
|
case '-':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Float - rightData.Float);
|
||||||
|
case '*':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Float * rightData.Float);
|
||||||
|
case '/':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Float / rightData.Float);
|
||||||
|
case '%':
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, l, leftData.Float % rightData.Float);
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvalReturn(EVAL_TYPE.NUMBER, DATA_TYPE.NONE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BEGExpr : Expr
|
||||||
|
{
|
||||||
|
|
||||||
|
VarExpr RHE;
|
||||||
|
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
|
||||||
|
EvalReturn r = RHE.evaluate(varStack);
|
||||||
|
|
||||||
|
write("Input: ");
|
||||||
|
string input = strip(readln());
|
||||||
|
|
||||||
|
if (isNumeric(input))
|
||||||
|
{
|
||||||
|
if (r.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
varStack[r.sVar] = Variable();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
varStack[r.sVar].iVal = to!int(input);
|
||||||
|
varStack[r.sVar].dataType = DATA_TYPE.INTEGER;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (ConvException e)
|
||||||
|
{
|
||||||
|
varStack[r.sVar].fVal = to!float(input);
|
||||||
|
varStack[r.sVar].dataType = DATA_TYPE.FLOAT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new RunException("Error! Cannot convert input");
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvalReturn(EVAL_TYPE.BEG, DATA_TYPE.NONE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PRINTExpr : Expr
|
||||||
|
{
|
||||||
|
|
||||||
|
VarExpr RHE;
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
EvalReturn r = RHE.evaluate(varStack);
|
||||||
|
if (r.type == EVAL_TYPE.UNDEFINED_VAR)
|
||||||
|
{
|
||||||
|
writeln("SNOL> Error! [" ~ r.sVar ~ "] is not defined!");
|
||||||
|
}
|
||||||
|
else if (r.type == EVAL_TYPE.VAR)
|
||||||
|
{
|
||||||
|
Variable* v = (r.sVar in varStack);
|
||||||
|
if (v.dataType == DATA_TYPE.INTEGER)
|
||||||
|
writeln("SNOL> [" ~ r.sVar ~ "] = ", v.iVal);
|
||||||
|
else
|
||||||
|
writeln("SNOL> [" ~ r.sVar ~ "] = ", v.fVal);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return EvalReturn(EVAL_TYPE.PRINT, DATA_TYPE.NONE, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EXITExpr : Expr
|
||||||
|
{
|
||||||
|
override EvalReturn evaluate(ref Variable[string] varStack)
|
||||||
|
{
|
||||||
|
return EvalReturn(EVAL_TYPE.EXIT, DATA_TYPE.NONE, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
module lexer;
|
||||||
|
import std.stdio;
|
||||||
|
import core.stdc.ctype;
|
||||||
|
import std.string;
|
||||||
|
|
||||||
|
enum TOKEN_TYPE
|
||||||
|
{
|
||||||
|
OPERATOR,
|
||||||
|
INTEGER,
|
||||||
|
FLOAT,
|
||||||
|
VARIABLE,
|
||||||
|
PROCEDURE,
|
||||||
|
|
||||||
|
UNKNOWN,
|
||||||
|
END_OF_TEXT
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Token
|
||||||
|
{
|
||||||
|
TOKEN_TYPE type;
|
||||||
|
string data;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const string OPERATORS = "=+-*/%";
|
||||||
|
|
||||||
|
struct Lexer
|
||||||
|
{
|
||||||
|
long position = 0;
|
||||||
|
private string textToTokenize;
|
||||||
|
this(string textToTokenize)
|
||||||
|
{
|
||||||
|
this.textToTokenize = textToTokenize;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Token getNextToken()
|
||||||
|
{
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
return Token(TOKEN_TYPE.END_OF_TEXT, null);
|
||||||
|
string dataStr;
|
||||||
|
char lastChar = textToTokenize[position];
|
||||||
|
|
||||||
|
while (isspace(lastChar))
|
||||||
|
{
|
||||||
|
position++;
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
return Token(TOKEN_TYPE.END_OF_TEXT, null);
|
||||||
|
lastChar = textToTokenize[position];
|
||||||
|
}
|
||||||
|
if (isalpha(lastChar))
|
||||||
|
{
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
lastChar = textToTokenize[++position];
|
||||||
|
while (isalnum(lastChar))
|
||||||
|
{
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
|
||||||
|
lastChar = textToTokenize[position];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataStr == "BEG")
|
||||||
|
return Token(TOKEN_TYPE.PROCEDURE, dataStr);
|
||||||
|
|
||||||
|
else if (dataStr == "PRINT")
|
||||||
|
{
|
||||||
|
return Token(TOKEN_TYPE.PROCEDURE, dataStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (dataStr == "EXIT" && lastChar == '!')
|
||||||
|
{
|
||||||
|
position++;
|
||||||
|
return Token(TOKEN_TYPE.PROCEDURE, dataStr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
return Token(TOKEN_TYPE.VARIABLE, dataStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (indexOf(OPERATORS, lastChar) > -1)
|
||||||
|
{
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
return Token(TOKEN_TYPE.OPERATOR, dataStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isdigit(lastChar))
|
||||||
|
{
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
return Token(TOKEN_TYPE.INTEGER, dataStr);
|
||||||
|
lastChar = textToTokenize[position];
|
||||||
|
while (isdigit(lastChar))
|
||||||
|
{
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
return Token(TOKEN_TYPE.INTEGER, dataStr);
|
||||||
|
lastChar = textToTokenize[position];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastChar == '.')
|
||||||
|
{
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
{
|
||||||
|
position--;
|
||||||
|
return Token(TOKEN_TYPE.INTEGER, dataStr);
|
||||||
|
}
|
||||||
|
lastChar = textToTokenize[position];
|
||||||
|
while (isdigit(lastChar))
|
||||||
|
{
|
||||||
|
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
if (position >= textToTokenize.length)
|
||||||
|
return Token(TOKEN_TYPE.FLOAT, dataStr);
|
||||||
|
lastChar = textToTokenize[position];
|
||||||
|
}
|
||||||
|
if(dataStr[$-1] == '.')
|
||||||
|
{
|
||||||
|
position--;
|
||||||
|
return Token(TOKEN_TYPE.INTEGER, dataStr[0..$-1]);
|
||||||
|
}
|
||||||
|
return Token(TOKEN_TYPE.FLOAT, dataStr);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Token(TOKEN_TYPE.INTEGER, dataStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dataStr ~= lastChar;
|
||||||
|
position++;
|
||||||
|
return Token(TOKEN_TYPE.UNKNOWN, dataStr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,291 @@
|
||||||
|
module parser;
|
||||||
|
import lexer;
|
||||||
|
import ast;
|
||||||
|
import std.stdio;
|
||||||
|
import std.conv;
|
||||||
|
import std.string;
|
||||||
|
|
||||||
|
class ParseException : Exception
|
||||||
|
{
|
||||||
|
this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure nothrow @nogc @safe
|
||||||
|
{
|
||||||
|
super(msg, file, line, nextInChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Parser
|
||||||
|
{
|
||||||
|
Lexer lex;
|
||||||
|
Token[] tokens = [];
|
||||||
|
Expr[] expr = [];
|
||||||
|
|
||||||
|
this(string textToParse)
|
||||||
|
{
|
||||||
|
lex = Lexer(textToParse);
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr parse()
|
||||||
|
{
|
||||||
|
syntaxSanityCheck();
|
||||||
|
buildAST();
|
||||||
|
|
||||||
|
|
||||||
|
Expr topExpr = null;
|
||||||
|
if(expr.length < 1)
|
||||||
|
return null;
|
||||||
|
foreach (Expr key; expr)
|
||||||
|
{
|
||||||
|
if (key.masterExpr is null)
|
||||||
|
{
|
||||||
|
topExpr = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return topExpr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void syntaxSanityCheck()
|
||||||
|
{
|
||||||
|
Token currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
while (currentToken.type != TOKEN_TYPE.END_OF_TEXT)
|
||||||
|
{
|
||||||
|
//writeln(currentToken);
|
||||||
|
|
||||||
|
if (currentToken.type == TOKEN_TYPE.PROCEDURE && currentToken.data == "BEG")
|
||||||
|
{
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.VARIABLE)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.END_OF_TEXT)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (currentToken.type == TOKEN_TYPE.PROCEDURE && currentToken.data == "EXIT")
|
||||||
|
{
|
||||||
|
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.END_OF_TEXT)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (currentToken.type == TOKEN_TYPE.PROCEDURE && currentToken.data == "PRINT")
|
||||||
|
{
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.VARIABLE)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.END_OF_TEXT)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (currentToken.type == TOKEN_TYPE.OPERATOR)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (currentToken.data == "=")
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((tokens.length > 1 || tokens.length == 0) && tokens[0].type != TOKEN_TYPE
|
||||||
|
.VARIABLE)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (currentToken.data == "-")
|
||||||
|
{
|
||||||
|
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.VARIABLE && currentToken.type != TOKEN_TYPE.INTEGER && currentToken
|
||||||
|
.type != TOKEN_TYPE.FLOAT)
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
if (tokens.length <= 2 || (tokens[$ - 3].type != TOKEN_TYPE.VARIABLE && tokens[$ - 3].type != TOKEN_TYPE
|
||||||
|
.INTEGER && tokens[$ - 3].type != TOKEN_TYPE.FLOAT))
|
||||||
|
{
|
||||||
|
tokens[$ - 2].data = 'u' ~ tokens[$ - 2].data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (currentToken.data == "+")
|
||||||
|
{
|
||||||
|
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.VARIABLE && currentToken.type != TOKEN_TYPE.INTEGER && currentToken
|
||||||
|
.type != TOKEN_TYPE.FLOAT)
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
if (tokens.length <= 2 || (tokens[$ - 3].type != TOKEN_TYPE.VARIABLE && tokens[$ - 3].type != TOKEN_TYPE
|
||||||
|
.INTEGER && tokens[$ - 3].type != TOKEN_TYPE.FLOAT))
|
||||||
|
{
|
||||||
|
tokens[$ - 2].data = 'u' ~ tokens[$ - 2].data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tokens[$ - 2].type != TOKEN_TYPE.VARIABLE && tokens[$ - 2].type != TOKEN_TYPE.INTEGER && tokens[$ - 2]
|
||||||
|
.type != TOKEN_TYPE.FLOAT)
|
||||||
|
throw new ParseException(
|
||||||
|
"Unknown command! Does not match any valid");
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
/*if(currentToken.type!=TOKEN_TYPE.VARIABLE && currentToken.type!=TOKEN_TYPE.INTEGER && currentToken.type!=TOKEN_TYPE.FLOAT)
|
||||||
|
throw new ParseException("Wrong usage of '"~tokens[$-1].data~"' operator");*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (currentToken.type == TOKEN_TYPE.FLOAT || currentToken.type == TOKEN_TYPE.INTEGER || currentToken
|
||||||
|
.type == TOKEN_TYPE.VARIABLE)
|
||||||
|
{
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
if (currentToken.type != TOKEN_TYPE.OPERATOR && currentToken.type != TOKEN_TYPE
|
||||||
|
.END_OF_TEXT)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new ParseException("Unknown command! Does not match any valid command of the language.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if( currentToken.type == TOKEN_TYPE.UNKNOWN)
|
||||||
|
{
|
||||||
|
throw new ParseException("Unexpected token: " ~ currentToken.data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
currentToken = lex.getNextToken();
|
||||||
|
tokens ~= currentToken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void opMaker(int id)
|
||||||
|
{
|
||||||
|
OpExpr opExpr = cast(OpExpr) expr[id];
|
||||||
|
if (!opExpr.unary)
|
||||||
|
{
|
||||||
|
Expr leftExpr = expr[id - 1];
|
||||||
|
while (leftExpr.masterExpr !is null)
|
||||||
|
{
|
||||||
|
leftExpr = leftExpr.masterExpr;
|
||||||
|
}
|
||||||
|
leftExpr.masterExpr = opExpr;
|
||||||
|
opExpr.LHE = leftExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr rightExpr = expr[id + 1];
|
||||||
|
while (rightExpr.masterExpr !is null)
|
||||||
|
{
|
||||||
|
rightExpr = rightExpr.masterExpr;
|
||||||
|
}
|
||||||
|
rightExpr.masterExpr = opExpr;
|
||||||
|
opExpr.RHE = rightExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void buildAST()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < tokens.length; i++)
|
||||||
|
{
|
||||||
|
if (tokens[i].type == TOKEN_TYPE.END_OF_TEXT)
|
||||||
|
break;
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.FLOAT)
|
||||||
|
expr ~= new FloatExpr(to!float(tokens[i].data));
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.INTEGER)
|
||||||
|
expr ~= new IntExpr(to!int(tokens[i].data));
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.OPERATOR && tokens[i].data[0] == 'u')
|
||||||
|
expr ~= new OpExpr(to!char(tokens[i].data[1]), true);
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.OPERATOR)
|
||||||
|
expr ~= new OpExpr(to!char(tokens[i].data));
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.VARIABLE)
|
||||||
|
expr ~= new VarExpr(to!string(tokens[i].data));
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.PROCEDURE && tokens[i].data == "BEG")
|
||||||
|
expr ~= new BEGExpr();
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.PROCEDURE && tokens[i].data == "PRINT")
|
||||||
|
expr ~= new PRINTExpr();
|
||||||
|
else if (tokens[i].type == TOKEN_TYPE.PROCEDURE && tokens[i].data == "EXIT")
|
||||||
|
expr ~= new EXITExpr();
|
||||||
|
|
||||||
|
}
|
||||||
|
if(expr.length < 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < expr.length; i++) // unary
|
||||||
|
{
|
||||||
|
if (typeid(expr[i]) == typeid(OpExpr) && (cast(OpExpr) expr[i]).unary)
|
||||||
|
{
|
||||||
|
opMaker(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < expr.length; i++) // * / %
|
||||||
|
{
|
||||||
|
if (typeid(expr[i]) == typeid(OpExpr) && indexOf("*/%", (cast(OpExpr) expr[i]).op) > -1 && !(
|
||||||
|
cast(OpExpr) expr[i]).unary)
|
||||||
|
{
|
||||||
|
opMaker(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
for (int i = 0; i < expr.length; i++) // + -
|
||||||
|
{
|
||||||
|
if (typeid(expr[i]) == typeid(OpExpr) && indexOf("+-", (cast(OpExpr) expr[i]).op) > -1 && !(
|
||||||
|
cast(OpExpr) expr[i]).unary)
|
||||||
|
{
|
||||||
|
opMaker(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
for (int i = 0; i < expr.length; i++) // =
|
||||||
|
{
|
||||||
|
if (typeid(expr[i]) == typeid(OpExpr) && indexOf("=", (cast(OpExpr) expr[i]).op) > -1 && !(
|
||||||
|
cast(OpExpr) expr[i]).unary)
|
||||||
|
{
|
||||||
|
opMaker(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//PROCEDURES expr
|
||||||
|
if (typeid(expr[0]) == typeid(BEGExpr))
|
||||||
|
{
|
||||||
|
BEGExpr begExpr = (cast(BEGExpr) expr[0]);
|
||||||
|
VarExpr rightExpr = cast(VarExpr) expr[1];
|
||||||
|
rightExpr.masterExpr = begExpr;
|
||||||
|
begExpr.RHE = rightExpr;
|
||||||
|
}
|
||||||
|
else if (typeid(expr[0]) == typeid(PRINTExpr))
|
||||||
|
{
|
||||||
|
PRINTExpr printExpr = (cast(PRINTExpr) expr[0]);
|
||||||
|
VarExpr rightExpr = cast(VarExpr) expr[1];
|
||||||
|
rightExpr.masterExpr = printExpr;
|
||||||
|
printExpr.RHE = rightExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
module runtimedata;
|
||||||
|
|
||||||
|
|
||||||
|
enum DEBUG = true;
|
||||||
|
|
||||||
|
class RunException : Exception
|
||||||
|
{
|
||||||
|
this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure nothrow @nogc @safe
|
||||||
|
{
|
||||||
|
super(msg, file, line, nextInChain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum DATA_TYPE
|
||||||
|
{
|
||||||
|
FLOAT,
|
||||||
|
INTEGER,
|
||||||
|
VAR_POINTER,
|
||||||
|
NONE
|
||||||
|
}
|
||||||
|
enum EVAL_TYPE
|
||||||
|
{
|
||||||
|
NUMBER,
|
||||||
|
UNDEFINED_VAR,
|
||||||
|
VAR,
|
||||||
|
PRINT,
|
||||||
|
BEG,
|
||||||
|
ASSIGN,
|
||||||
|
EXIT
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EvalReturn
|
||||||
|
{
|
||||||
|
this(EVAL_TYPE type,DATA_TYPE dataType, float fVal )
|
||||||
|
{
|
||||||
|
this.type=type;
|
||||||
|
this.dataType = dataType;
|
||||||
|
this.fVal = fVal;
|
||||||
|
}
|
||||||
|
this(EVAL_TYPE type,DATA_TYPE dataType, int iVal)
|
||||||
|
{
|
||||||
|
this.type=type;
|
||||||
|
this.dataType = dataType;
|
||||||
|
this.iVal = iVal;
|
||||||
|
|
||||||
|
}
|
||||||
|
this(EVAL_TYPE type,DATA_TYPE dataType, string sVar)
|
||||||
|
{
|
||||||
|
this.type=type;
|
||||||
|
this.dataType = dataType;
|
||||||
|
this.sVar = sVar;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
EVAL_TYPE type;
|
||||||
|
DATA_TYPE dataType;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
float fVal;
|
||||||
|
int iVal;
|
||||||
|
string sVar;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Variable
|
||||||
|
{
|
||||||
|
DATA_TYPE dataType;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
float fVal;
|
||||||
|
int iVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue