def auto_download_plc_programs(): """Downloads plc programs to track controllers""" logger.critical("Auto downloading plc programs") for i, track_controller in enumerate(track_system.green_track_controllers): if type(track_controller) != HWTrackCtrlConnector: source_code = '' for line in open( 'resources/Track Controller PLC Programs/Green{}.txt'. format(i)): source_code += line output_file = 'CompiledOutput.txt' lex = Lexer(source_code) emitter = Emitter(output_file) par = Parser(lex, emitter) par.program("Green{}".format(i)) track_controller.download_program(output_file) for i, track_controller in enumerate(track_system.red_track_controllers): source_code = '' for line in open( 'resources/Track Controller PLC Programs/Red{}.txt'.format( i + 12)): source_code += line output_file = 'CompiledOutput.txt' lex = Lexer(source_code) emitter = Emitter(output_file) par = Parser(lex, emitter) par.program("Red{}".format(i + 12)) track_controller.download_program(output_file)
def test_peek(): """Tests the peek method PRECONDITIONS: Lexer made with input "test input" EXECUTION: lex.peek() POSTCONDITIONS: lex.peek returns 'e' lex.current_character remains 't' """ test_input = "test input" lex = Lexer(test_input) assert test_input[1] == lex.peek() assert test_input[0] == lex.current_character
def test_next_character(): """Tests the next_character method PRECONDITIONS: Lexer made with input "test input" EXECUTION: Call next_character repeatedly POSTCONDITIONS: current_character matches character in "test input" current_position increments once per call """ test_input = "test input" lex = Lexer(test_input) assert test_input + '\n' == lex.source_code assert test_input[0] == lex.current_character assert lex.current_position == 0 for i, char in enumerate(test_input[1:]): lex.next_character() assert char == lex.current_character assert (i + 1) == lex.current_position lex.next_character() assert '\n' == lex.current_character lex.next_character() assert '\0' == lex.current_character
def compile_program(file_name): """Method used to invoke the PLC language compiler on the given file :param str file_name: Absolute path to the file to compile :return: Name of the output file. None if compilation failed """ # Gather the source code from the file source_code = '' for line in open(file_name, 'r'): source_code = ''.join([source_code, line]) # Create compiler elements output_file = 'CompiledOutput.txt' lex = Lexer(source_code) emitter = Emitter(output_file) par = Parser(lex, emitter) # Try to compile try: par.program( program_name=os.path.splitext(os.path.basename(file_name))[0]) return output_file except CompilationError as compilation_error: alert = Alert("Compilation failed with error:\n {}".format( str(compilation_error))) alert.exec_() return None
def test_simple_program(): """Tests the compilation of a simple program""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) par.program() assert par.emitter.requests == 'START_DOWNLOAD\n' \ 'CREATE_TAG input1 FALSE\n' \ 'CREATE_TAG output1 FALSE\n' \ 'CREATE_TASK PERIOD 1000 myTask\n' \ 'CREATE_ROUTINE Main\n' \ 'CREATE_RUNG\n' \ 'CREATE_INSTRUCTION XIO input1\n' \ 'CREATE_INSTRUCTION OTE output1\n' \ 'END_DOWNLOAD\n'
def test_multiple_mains(): """Tests a program with multiple mains""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #10 : There can " \ "only be one Main routine" == str(pytest_wrapped_e.value)
def test_statement_tag_1(mock_emitter): """Test the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: No exception is thrown """ source_code = "TAG myTag = TRUE\nTAG myTag = FALSE" par = Parser(Lexer(source_code), mock_emitter) par.program()
def test_statement_task_2(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: No exception is thrown """ source_code = "TASK<EVENT=myEvent> myTask" par = Parser(Lexer(source_code), mock_emitter) par.statement()
def test_statement_routine_success(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: No exception is thrown """ source_code = "ROUTINE Main" par = Parser(Lexer(source_code), mock_emitter) par.stack.append('TASK') par.statement()
def test_statement_rung_2(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: No exception is thrown """ source_code = "RUNG myRung" par = Parser(Lexer(source_code), mock_emitter) par.stack.append('ROUTINE') par.statement()
def test_statement_task_3(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: Exception is thrown with correct error message """ source_code = "TASK<CONTINUOUS> myTask" par = Parser(Lexer(source_code), mock_emitter) with pytest.raises(CompilationError) as pytest_wrapped_e: par.statement() assert "Parsing error line #0 : Invalid task type CONTINUOUS" == str(pytest_wrapped_e.value)
def test_statement_tag_3(mock_emitter): """Test for a tag name being too long PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: Exception is thrown with correct error message """ source_code = "TAG myLongTagName = FALSE" par = Parser(Lexer(source_code), mock_emitter) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #0 : Tag name myLongTagName "\ "too long. The limit is 7 characters" == str(pytest_wrapped_e.value)
def test_statement_tag_2(mock_emitter): """Test the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: Exception is thrown with correct error message """ source_code = "TAG myTag = notAKeyword" par = Parser(Lexer(source_code), mock_emitter) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #1 : Expected "\ "FALSE, but found notAKeyword" == str(pytest_wrapped_e.value)
def test_statement_end(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: No exception is thrown """ source_code = "ENDRUNG\nENDROUTINE\nENDTASK" par = Parser(Lexer(source_code), mock_emitter) par.stack.append('TASK') par.stack.append('ROUTINE') par.stack.append('RUNG') par.main_flag = True par.program()
def test_statement_routine_failure(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: Exception is thrown """ source_code = "ROUTINE " par = Parser(Lexer(source_code), mock_emitter) par.stack.append('TASK') with pytest.raises(CompilationError) as pytest_wrapped_e: par.statement() assert "Parsing error line #1 : Expected "\ "IDENTIFIER, but found \n" == str(pytest_wrapped_e.value)
def test_statement_instructions(mock_emitter): """Tests the statement method PRECONDITIONS: Create parser with line source_code EXECUTION: par.statement() POSTCONDITION: No exception is thrown """ source_code = "XIC tag\nXIO tag\nOTE tag\nOTL tag\nOTU tag\nJSR routine\nEMIT event\nRET" par = Parser(Lexer(source_code), mock_emitter) # Add tag to the symbols to avoid errors par.tags.add('tag') par.events.add('event') par.routines.add('routine') par.program()
def test_get_token_failure_1(): """Tests the get_token method PRECONDITIONS: Lexer made with input "TASK<PERIOD=10.> myTask # This is my task" EXECUTION: lex.get_token() repeatedly POSTCONDITIONS: sys.exit is called """ test_input = "TASK<PERIOD=10.> myTask # This is my task" lex = Lexer(test_input) lex.get_token() lex.get_token() lex.get_token() lex.get_token() with pytest.raises(CompilationError) as pytest_wrapped_e: lex.get_token() assert "Lexing error line #1 : Illegal character in number" == str( pytest_wrapped_e.value)
def test_missing_end(): """Test program with missing ENDROUTINE""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #10 : Missing matching ENDROUTINE" == str( pytest_wrapped_e.value)
def test_no_tag(): """Test program that uses a nonexistent tag""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input2\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #6 : Referencing tag "\ "input2 before assignment" == str(pytest_wrapped_e.value)
def test_low_period(): """Test program with a period less than 20ms""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=10> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #2 : Period below allowable limit 20" == str( pytest_wrapped_e.value)
def test_missing_main(): """Tests a program that is missing a main routine""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE myRoutine\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #11 : There must be a " \ "single Main routine" == str(pytest_wrapped_e.value)
def test_nonexistent_event(): """Test program that uses a nonexistent event""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "EMIT MissingEvent\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #12 : Emitted event MissingEvent does not " \ "correspond to a task" == str(pytest_wrapped_e.value)
def test_nonexistent_routine(): """Test program that jumps to a nonexistent routine""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "JSR MissingRoutine\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #12 : Routine "\ "MissingRoutine does not exist" == str(pytest_wrapped_e.value)
def test_too_many_ends(): """Test program that too many end statements""" code = "TAG input1 = FALSE\n" \ "TAG output1 = FALSE\n" \ "TASK<PERIOD=1000> myTask\n" \ "ROUTINE Main\n" \ "RUNG\n" \ "XIO input1\n" \ "OTE output1\n" \ "ENDRUNG\n" \ "ENDROUTINE\n" \ "ENDTASK\n" \ "ENDTASK\n" lex = Lexer(code) emit = Emitter('TestProgram') par = Parser(lex, emit) with pytest.raises(CompilationError) as pytest_wrapped_e: par.program() assert "Parsing error line #12 : Too many end statements" == str( pytest_wrapped_e.value)
def test_get_token_success(): """Tests the get_token method PRECONDITIONS: Lexer made with input "TASK<PERIOD=10.50> myTask # This is my task" EXECUTION: lex.get_token() repeatedly POSTCONDITIONS: get_token() returns the correct token """ test_input = "TASK<PERIOD=10.50> myTask # This is my task" lex = Lexer(test_input) token = lex.get_token() assert TokenType.TASK == token.type assert "TASK" == token.text token = lex.get_token() assert TokenType.OPEN_ANGLE == token.type assert "<" == token.text token = lex.get_token() assert TokenType.PERIOD == token.type assert "PERIOD" == token.text token = lex.get_token() assert TokenType.EQ == token.type assert "=" == token.text token = lex.get_token() assert TokenType.NUMBER == token.type assert "10.50" == token.text token = lex.get_token() assert TokenType.CLOSE_ANGLE == token.type assert ">" == token.text token = lex.get_token() assert TokenType.IDENTIFIER == token.type assert "myTask" == token.text token = lex.get_token() assert TokenType.NEWLINE == token.type assert "\n" == token.text token = lex.get_token() assert TokenType.EOF == token.type assert "" == token.text