-
Notifications
You must be signed in to change notification settings - Fork 7
Demo of clang AST #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "uuid": "34292487-5019-47ab-8175-49e9dada8790", | ||
| "title": "Sale Price", | ||
| "topic": "Variables", | ||
| "singleVariant": true, | ||
| "tags": [ "jgfoster" ], | ||
| "type": "v3", | ||
| "gradingMethod": "External", | ||
| "externalGradingOptions": { | ||
| "enabled": true, | ||
| "image": "prairielearn/grader-c", | ||
| "entrypoint": "/grade/tests/test.sh", | ||
| "serverFilesCourse": ["clang/"] | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| <pl-question-panel> | ||
| <p>Jimmy is at Hollister and he wants to buy a hoodie. | ||
| The hoodie costs $65, but it has a discount of 15%.</p> | ||
| <p>Write a program to help Jimmy calculate the sale price of the hoodie. | ||
| The program must ask Jimmy for the item price, and then it will display | ||
| the sale price with a precision of 2 decimal places.</p> | ||
| <p>You <b>must</b> declare a double constant variable and assign it with the sale percentage.</p> | ||
| <p>You <b>must</b> declare the sale percentage using scientific notation. | ||
| (No, this isn't how you would normally write a program like this, but it is a convenient demo.) | ||
| </p> | ||
| <p>Below is an example of the program:</p> | ||
|
|
||
| <pl-code language="output"> | ||
| Enter an item price: $65.00 | ||
| The sale price is $55.25 | ||
|
|
||
| </pl-code> | ||
| </pl-question-panel> | ||
|
|
||
| <pl-file-editor source-file-name="source.cpp" file-name="student.cpp" ace-mode="ace/mode/c_cpp"></pl-file-editor> | ||
|
|
||
| <pl-submission-panel> | ||
| <pl-external-grader-results></pl-external-grader-results> | ||
| <pl-file-preview></pl-file-preview> | ||
| </pl-submission-panel> |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,13 @@ | ||||||
| #include <iostream> | ||||||
| #include <iomanip> | ||||||
| using namespace std; | ||||||
|
|
||||||
| int main() { | ||||||
| const double DISCOUNT = 1.5e-1; | ||||||
| double price; | ||||||
|
|
||||||
| cout << "Enter an item price: $"; | ||||||
| cin >> price; | ||||||
| cout << fixed << setprecision(2) << price * (1.0 - DISCOUNT) << endl; | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The example includes a text, the solution should probably include it too, even if it's not enforced in the autograder.
Suggested change
|
||||||
| return 0; | ||||||
| } | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| #include <iostream> | ||
| using namespace std; | ||
|
|
||
| int main() { | ||
|
|
||
| // Your solution goes here | ||
|
|
||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,93 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #! /usr/bin/python3 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import cgrader | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import clang.cindex | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clang.cindex.Config.set_library_path("/usr/lib/") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def print_ast(node, indent=0): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print(" " * indent + str(node.spelling) + " (" + str(node.kind) + ")") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for child in node.get_children(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print_ast(child, indent + 2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def find_const_double(node): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node.kind == clang.cindex.CursorKind.VAR_DECL | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and "double" in node.type.spelling | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| and node.type.is_const_qualified() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return node | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for child in node.get_children(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| found = find_const_double(child) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if found is not None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return found | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def var_name_is_valid(node): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return not any(c.islower() for c in node.spelling) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def find_double_initializer(node): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| child = list(node.get_children())[0] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return child if child.kind == clang.cindex.CursorKind.FLOATING_LITERAL else None | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def initializer_is_scientific_notation(node): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tokens = list(node.get_tokens()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if tokens: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| literal = tokens[0].spelling | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return "e" in literal or "E" in literal | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return False | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class QuestionGrader(cgrader.CPPGrader): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| def tests(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| index = clang.cindex.Index.create() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| translation_unit = index.parse("student.cpp", args=["-x", "c++"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # print_ast(translation_unit.cursor) # Uncomment to print the AST (be sure to comment out includes!) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| decl = find_const_double(translation_unit.cursor) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if decl is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("const double not found", points=0, max_points=1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("const double found") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not var_name_is_valid(decl): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("variable name is not using the capitalization expected for a constant variable", points=0, max_points=1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("variable name is valid") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| initializer = find_double_initializer(decl) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if initializer is None: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("variable does not have a double initializer", points=0, max_points=1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("variable has a double initializer") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not initializer_is_scientific_notation(initializer): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("initializer is not in scientific notation", points=0, max_points=1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self.add_test_result("initializer is in scientific notation") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| decl = find_const_double(translation_unit.cursor) | |
| if decl is None: | |
| self.add_test_result("const double not found", points=0, max_points=1) | |
| return | |
| self.add_test_result("const double found") | |
| if not var_name_is_valid(decl): | |
| self.add_test_result("variable name is not using the capitalization expected for a constant variable", points=0, max_points=1) | |
| return | |
| self.add_test_result("variable name is valid") | |
| initializer = find_double_initializer(decl) | |
| if initializer is None: | |
| self.add_test_result("variable does not have a double initializer", points=0, max_points=1) | |
| return | |
| self.add_test_result("variable has a double initializer") | |
| if not initializer_is_scientific_notation(initializer): | |
| self.add_test_result("initializer is not in scientific notation", points=0, max_points=1) | |
| return | |
| self.add_test_result("initializer is in scientific notation") | |
| decl = find_const_double(translation_unit.cursor) | |
| self.add_test_result("const double found", points=(decl is not None)) | |
| var_name_result = decl is not None and var_name_is_valid(decl) | |
| self.add_test_result("variable name is valid", | |
| points=1 if var_name_result else 0, | |
| output=None if var_name_result else "variable name is not using the capitalization expected for a constant variable" | |
| ) | |
| self.add_test_result("variable has a double initializer", | |
| points=1 if decl is not None and find_double_initializer(decl) else 0 | |
| ) | |
| self.add_test_result("initializer is in scientific notation", | |
| points=1 if decl is not None and initializer_is_scientific_notation(initializer) else 0 | |
| ) |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not needed when there is a single output.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the ast tests pass, this will always return true even if the code doesn't print anything valid.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| #! /bin/bash | ||
| export PATH=/grade/serverFilesCourse:$PATH | ||
| ln -s /usr/lib/llvm-18/lib/libclang.so.1 /usr/lib/libclang.so | ||
| python3 /grade/tests/test.py |
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line and the serverFilesCourse content become unnecessary once PrairieLearn/PrairieLearn#11913 is handled. Also, the entrypoint can become the Python file again if the path does not need to be updated.