Skip to content

Commit

Permalink
Add relation and equality operators
Browse files Browse the repository at this point in the history
  • Loading branch information
matedabis committed Feb 19, 2019
1 parent d3fc686 commit 513310b
Show file tree
Hide file tree
Showing 3 changed files with 259 additions and 0 deletions.
245 changes: 245 additions & 0 deletions src/scripts/generate_relation_and_equality_ops.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
#!/usr/bin/env python3

# Copyright JS Foundation and other contributors, http://js.foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import sys
import argparse
import collections
import random
import subprocess

# Local packages
import settings
path = os.path.join(settings.TEMPLATE_DIR)
sys.path.append(path)
import validate # Function
from constants import _constants # Constant values
from parse_args import parse_args # Argument parser
from generator_base import Generator # Base class
from utils import write_file # File writer
from generate_binary_ops import GenSequence # Operation result object
from utils import Messages # Messages

# Class for relational and equality expressions
class GenRelationAndEquality(Generator):

class _InnerValues():
generated_filename = '''relation-equality-ops-{NUMBER}.js''' # The file name prefix of the generated
relational_and_equality_operators = ["EQ", "NE", "SE", "SN", "LT", "LE", "GT", "GE"]
# Special values which need to be handled differently than numbers or strings
special_values = ["undefined", "null", "NaN", "true", "false"]

# Initialize the generator
def __init__(self, options):
self.num_operands = options.operand_count # Number of operands in the expression
self.options = options

def expression_generator(self):
self.functions_lib()
self.expression_builder()

# Libary of operator functions
def functions_lib(self):
self.operators_list = [] # List of currently used operators
self.oper_funcs = {}
for oper in self._InnerValues.relational_and_equality_operators:
self.oper_funcs[oper] = getattr(self, oper)

# Handler for operand generating
def percentage(self, opts, i):
sum = 0
for elem in opts:
sum += elem[0]
if i <= sum:
break;
if sum >= 100:
break
return elem[1]

# Generating the expression with operands and operators
def expression_builder(self):
for op in self.options.test_cases:
self.operators_list.append(self.oper_funcs[op])

self.operands = [0] * self.num_operands # Creating a list for the operands
self.operators = [0] * (self.num_operands - 1) # Creating a list for the operators

# Generating the expression
for i in range(self.num_operands - 1):
options = [
[40, self._InnerValues.special_values[random.randint(0, len(self._InnerValues.special_values) - 1)]],
[10, str(random.randint(_constants.min, _constants.max))],
[50, random.randint(_constants.min, _constants.max)],
]
self.operands[i] = self.percentage(options, random.randint(0, 100))
self.operators[i] = random.randint(0, len(self.operators_list) - 1)
self.operands[self.num_operands - 1] = self.percentage(options, random.randint(0, 100))

# Convert python boolean values to fit js
def convert_true_false_to_lowercase_string(self, value):
return "true" if value else "false";

# Add commas to the value if it is meant to be a string in the test case
def add_coma_if_string(self, value):
if value not in self._InnerValues.special_values and isinstance(value, str):
value = "'%s'" % (value)
return value

# A helper function for special cases in equality
def eq_helper_special_cases(self, values):
special_cases = [["undefined", "undefined"], ["null", "null"], ["undefined", "null"],
["null", "undefined"], ["true", "true"], ["false", "false"]]
return values in special_cases

# ES v5.1 11.8. - 11.9. (except 11.8.6. and 11.8.7.)
# Relational and equality operators

# Equality operators

# ES v5.1 11.9.3
# Equality
def EQ(self, left_value, right_value):
# If one of the values is NaN return false
if (left_value == "NaN" or right_value == "NaN"):
return GenSequence.create_operation_result_object(self ,'==', right_value, "false")

# Cases where it should return true
elif self.eq_helper_special_cases([left_value, right_value]):
return GenSequence.create_operation_result_object(self ,'==', right_value, "true")

# If it does not match the cases above and values are not undefined or null
elif left_value not in ["undefined", "null"] and right_value not in ["undefined", "null"]:
# Converting values
left_converted = int({"true": 1, "false": 0}.get(left_value, left_value))
right_converted = int({"true": 1, "false": 0}.get(right_value, right_value))
result = self.convert_true_false_to_lowercase_string(left_converted == right_converted)
return GenSequence.create_operation_result_object(self, '==', right_value, result)
return GenSequence.create_operation_result_object(self, '==', right_value, "false")

# ES v5.1 11.9.2.
# Not equality
def NE(self, left_value, right_value):
result = "true" if self.EQ(left_value, right_value)['result'] == "false" else "false"
return GenSequence.create_operation_result_object(self, '!=', right_value, result)

# ES v5.1 11.9.4
# Strict equality
def SE(self, left_value, right_value):
# If one of the values is NaN return false
if (left_value == "NaN" or right_value == "NaN"):
return GenSequence.create_operation_result_object(self ,'===', right_value, "false")
result = self.convert_true_false_to_lowercase_string(left_value == right_value)
return GenSequence.create_operation_result_object(self, '===', right_value, result)

# ES v5.1 11.9.5.
# Strict not equality
def SN(self, left_value, right_value):
result = "true" if self.SE(left_value, right_value)['result'] == "false" else "false"
return GenSequence.create_operation_result_object(self, '!==', right_value, result)

# Relational operators

# ES v5.1 11.8.5
# Less than operator
def LT(self, left_value, right_value):
# If one of the values is NaN return false
if left_value in ["NaN", "undefined"] or right_value in ["NaN", "undefined"]:
return GenSequence.create_operation_result_object(self ,'<', right_value, "false")
# To convert null, true, and false
# if its not null, true or false, dont do anything
left_converted = {"null": 0, "false": 0, "true": 1}.get(left_value, left_value)
right_converted = {"null": 0, "false": 0, "true": 1}.get(right_value, right_value)
if (isinstance(left_converted, str) and isinstance(right_converted, str)):
result = self.convert_true_false_to_lowercase_string(left_converted < right_converted)
else:
result = self.convert_true_false_to_lowercase_string(int(left_converted) < int(right_converted))
return GenSequence.create_operation_result_object(self, '<', right_value, result)

# ES v5.1 11.8.2
# Greater than operator
def GT(self, left_value, right_value):
if "NaN" in [left_value, right_value]:
return GenSequence.create_operation_result_object(self ,'>', right_value, "false")
result = self.LT(right_value, left_value)['result']
return GenSequence.create_operation_result_object(self, '>', right_value, result)

# ES v5.1 11.8.3.
# Less than or equal operator
def LE(self, left_value, right_value):
if left_value in ["NaN", "undefined"] or right_value in ["NaN", "undefined"]:
return GenSequence.create_operation_result_object(self ,'<=', right_value, "false")
result = "true" if self.LT(right_value, left_value)['result'] == "false" else "false"
return GenSequence.create_operation_result_object(self, '<=', right_value, result)

# ES v5.1 11.8.4.
# Greater than or equal operator
def GE(self, left_value, right_value):
if left_value in ["NaN", "undefined"] or right_value in ["NaN", "undefined"]:
return GenSequence.create_operation_result_object(self ,'>=', right_value, "false")
result = "true" if self.LT(left_value, right_value)['result'] == "false" else "false"
return GenSequence.create_operation_result_object(self, '>=', right_value, result)

# Declaration creater function
def create_declarations(self, expected_value):
# Adding false boolean string
false_boolean_string = self.create_false_boolean_string(expected_value)
# Adding commas if value is meant to be a string
for i in range(len(self.operands)):
self.operands[i] = self.add_coma_if_string(self.operands[i])
# Adding declarations of test values
test_case = self.create_declarations_string(self.create_declarations_dict(self.operands,
self.TEST_VALUE_VAR_NAME))
# Adding declarations of false value
test_case += self.create_declarations_string(self.create_declarations_dict(false_boolean_string,
self.FALSE_RESULT_VAR_NAME))
# Adding declarations of expected value
test_case += self.create_declarations_string(self.create_declarations_dict(expected_value,
self.EXPECTED_VAR_NAME))
return test_case

# Converting expression, expected value, false value in a function call to string format
def string_creator(self):
self.expression_generator()
last = self.operands[0]
expression = "%s%s" % ("(" * (self.num_operands - 1), self.TEST_VALUE_VAR_NAME.format(NUMBER = (1)))
for i in range(self.options.operand_count - 1):
result = self.operators_list[self.operators[i]](last, self.operands[i + 1])
expression += (" %s %s)" % (result["oper"], self.TEST_VALUE_VAR_NAME.format(NUMBER = (i + 2))))
self.operands[i + 1] = result["right_value"]
last = result["result"]
expression += "\n"
test_case = self.create_declarations(last)
# Adding the function call
test_case += self.create_function_call(expression, self.EXPECTED_VAR_NAME.format(NUMBER = ""),
self.FALSE_RESULT_VAR_NAME.format(NUMBER = ""), validate.validate_boolean_header)
test_case += "\n\n"
return test_case

# Relational and equality operator test generator function
def generate_relational_and_equality_ops(self, options):
random.seed(self.options.seed)
self.debug(Messages.generating, self.options)
gen_settings = settings.file_write_settings(self._InnerValues.generated_filename, self.options.output, validate.validate_boolean,
_constants.test_case_in_a_file)
for i in range(self.options.test_count): # In range of number test cases
self.test_case += self.string_creator()
self.append_test_case()
write_file(gen_settings, self.file_output)
self.debug(Messages.done, self.options)

if __name__ == '__main__':
gre = GenRelationAndEquality(parse_args(GenRelationAndEquality._InnerValues.relational_and_equality_operators)) # Declaring the generator
gre.generate_relational_and_equality_ops(GenSequence._InnerValues.generated_filename)
3 changes: 3 additions & 0 deletions src/scripts/generator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ def generate_false_numbers(self, expected_value, false_values_count, min_max):
false_values.append(self.generate_false_value(expected_value, min_max))
return false_values

def create_false_boolean_string(self, expected_value):
return "false" if expected_value == "true" else "true"

# Stringify an array of false values
def create_false_numbers_array_string(self, false_values_count):
return ('[%s]'% ', '.join([self.FALSE_RESULT_VAR_NAME.format(NUMBER = i) for i in range(1,
Expand Down
11 changes: 11 additions & 0 deletions src/scripts/templates/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@

validate_numeric_header = '''validate_numeric({EXPRESSION}, {EXPECTED_RESULT}, {FALSE_VALUES});'''

validate_boolean = '''
function validate_boolean(test_value, expected_result, false_result) {
assert(test_value === expected_result);
assert(test_value.toString() === expected_result.toString());
assert(test_value !== false_result);
assert(test_value.toString() !== false_result.toString());
}
'''

validate_boolean_header = '''validate_boolean({EXPRESSION}, {EXPECTED_RESULT}, {FALSE_VALUES});'''

test_function = '''
function test_function() {
return true;
Expand Down

0 comments on commit 513310b

Please sign in to comment.