CS253: Software Development with C++

Fall 2020

HW 3

CS253 HW3: Time to get classy!

Changes

Description

For this assignment, you will build on your HW1 work, and write a standalone class called Lexan, which will represent a lexical analyzer. Specifically, you will provide Lexan.h, which will contain the interface of that class, and the library libhw3.a, which will contain the implementation of that class.                 

The definitions of whitespace, comments, tokens, and the list of tokens is the same as HW1, except:

Methods

One method is forbidden:

no default ctor
The default (no-argument) ctor for Lexan must fail to compile. This is not a run-time error; it’s a compile-time error.

Lexan must have the following public methods:

Lexan(string)
Perform lexical analysis on the given multi-line string. If any bad tokens are detected, throw a runtime_error.
Copy constructor
Copy all information from another object of the same class.
Assignment operator
Copy all information from another object of the same class, replacing any previous information.
Destructor
Destroy.
.size()
Return a size_t representing the numer of tokens detected by the lexical analysis. For example, Lexan(" a +=#hi\nb ").size() would return a size_t with the value 3. Ignoring the result of .size() is a compile-time warning.
.empty()
Return true iff there are no tokens in this object. Ignoring the result of this method is a compile-time warning.
.clear()
Make this object have no tokens.
[size_t n]
The subscript operator returns a string representing the n th token produced by lexical analysis. If n is out of range, throw an out_of_range error mentioning the erroneous subscript, and the number of tokens in the object. [0] returns the first token, [1] returns the second, etc.

Non-methods:

ostream << Lexan
Write the tokens in this object to the ostream, separated by commas. Don’t add anything else. cout << Lexan("a≠b") must emit exactly a,≠,b.

Const-correctness, for arguments, methods, and operators, is your job. For example, it must be possible to call .size() on a const Lexan, or to copy a const Lexan to a non-const Lexan.                 

You may define other methods or data, public or private, as you see fit. You may define other classes, as you see fit. However, to use the Lexan class, the user need only #include "Lexan.h", not any other header files.                 

Unicode characters

Non-Requirements

Several things are not specified by this assignment. That means that the answer to these questions is “It’s up to you.”:

Debugging

If you encounter “STACK FRAME LINK OVERFLOW”, then try this:

    export STACK_FRAME_LINK_OVERRIDE=ffff-ad921d60486366258809553a3db49a4a

Libraries

libhw3.a is a library file. It contains a number of *.o (object) files. It must contain Lexan.o, but it may also contain whatever other *.o files you need. The CMakeLists.txt shown creates libhw3.a. It does not contain main().                 

Testing

You will have to write a main() function to test your code. Put it in a separate file, and do not make it part of libhw3.a. Particularly, do not put main() in Lexan.h or Lexan.cc. You will also have to create Lexan.h, and put it into hw3.tar. We will test your program by doing something like this:                 

    mkdir a-new-directory
    cd the-new-directory
    tar -x </some/where/else/hw3.tar
    cmake . && make
    cp /some/other/place/test-program.cc .
    g++ -Wall test-program.cc libhw3.a
    ./a.out

We will supply a main program to do the testing that we want. You should do something similar. It’s your choice whether to include your test program in your hw3.tar file. However, cmake . && make must work. If it fails because you didn’t package test.cc, but your CMakeLists.txt requires test.cc, then your build failed, and you get no points. Test your tar file, not just your code.                 

Sample Run

Here is a sample run, where % is my shell prompt:                 

% cat CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(hw3)

# Are we in the wrong directory?
if(CMAKE_SOURCE_DIR MATCHES "[Hh][Ww]([0-9])$")
   if(PROJECT_NAME MATCHES "[^${CMAKE_MATCH_1}]$")
      message(FATAL_ERROR "Building ${PROJECT_NAME} in ${CMAKE_SOURCE_DIR}")
   endif()
endif()

# Using -Wall is required:
add_compile_options(-Wall)

# These compile flags are highly recommended, but not required:
add_compile_options(-Wextra -Wpedantic)

# Optional super-strict mode:
add_compile_options(-fmessage-length=80 -fno-diagnostics-show-option
    -fstack-protector-all -g -O3 -std=c++17 -Walloc-zero -Walloca
    -Wctor-dtor-privacy -Wduplicated-cond -Wduplicated-branches
    -Werror -Wextra-semi -Wfatal-errors -Winit-self -Wlogical-op
    -Wold-style-cast -Wshadow -Wunused-const-variable=1
    -Wzero-as-null-pointer-constant)

# add_compile_options must be BEFORE add_executable.

# Create the executable from the source file main.cc:
add_library(${PROJECT_NAME} Lexan.cc)
add_executable(test test.cc)
target_link_libraries(test ${PROJECT_NAME})

# Create a tar file every time:
add_custom_target(${PROJECT_NAME}.tar ALL COMMAND
    tar -cf ${PROJECT_NAME}.tar test.cc Lexan.{cc,h} CMakeLists.txt)
% cat test.cc
#include "Lexan.h"
#include "Lexan.h"      // I meant to do that.
#include <iostream>

using namespace std;

int main() {
    const auto prog = R"(
        n  = a n += b C/=123 # z=zz

        ifn≤3returN1FI)";

    const Lexan lex(prog);
    for (size_t i=0; i<lex.size(); i++)
        cout << lex[i] << '\n';
    cout << lex << '\n';
    return 0;
}
% cmake .
… cmake output appears here …
% make
… make output appears here …
% ./test
n
=
a
n
+=
b
c
/=
123
if
n

3
return
1
fi
n,=,a,n,+=,b,c,/=,123,if,n,≤,3,return,1,fi

Requirements

If you have any questions about the requirements, ask. In the real world, your programming tasks will almost always be vague and incompletely specified. Same here.                 

Tar file

    cmake . && make

How to submit your work:

In Canvas, check in the file hw3.tar to the assignment “HW3”.                 

How to receive negative points:

Turn in someone else’s work.