-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add generator (from x86-64-assembly) (#65)
- Loading branch information
1 parent
12e6263
commit f430e7f
Showing
9 changed files
with
200 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,3 +6,4 @@ build/ | |
**/tests | ||
**/*.o | ||
*.pyc | ||
__pycache__/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#!/usr/bin/env bash | ||
set -e | ||
|
||
die() { echo "$*" >&2; exit 1; } | ||
|
||
if [[ $PWD != $(realpath "$(dirname "$0")/..") ]]; then | ||
die "You must be in the track root directory." | ||
fi | ||
if [[ -z $1 ]]; then | ||
die "usage: $0 exercise_slug" | ||
fi | ||
|
||
slug=$1 | ||
|
||
existing=$( jq --arg slug "${slug}" '.exercises.practice[] | select(.slug == $slug)' config.json ) | ||
if [[ -n ${existing} ]]; then | ||
die "${slug} already exists in config.json" | ||
fi | ||
|
||
pascal=${slug^} | ||
while [[ ${pascal} =~ (.*)-(.*) ]]; do | ||
pascal=${BASH_REMATCH[1]}${BASH_REMATCH[2]^} | ||
done | ||
|
||
if [[ -z $author ]]; then | ||
echo | ||
read -rp "What's your github username? " author | ||
fi | ||
|
||
read -p "What's the difficulty for ${slug}? " difficulty | ||
|
||
bin/fetch-configlet | ||
bin/configlet create --practice-exercise "${slug}" --author "${author}" --difficulty "${difficulty}" | ||
|
||
cp exercises/practice/hello-world/Makefile exercises/practice/${slug}/Makefile | ||
cp -r exercises/practice/hello-world/vendor exercises/practice/${slug}/vendor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,16 @@ | ||
# Instructions | ||
|
||
The classical introductory exercise. Just say "Hello, World!". | ||
The classical introductory exercise. | ||
Just say "Hello, World!". | ||
|
||
["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is | ||
the traditional first program for beginning programming in a new language | ||
or environment. | ||
["Hello, World!"][hello-world] is the traditional first program for beginning programming in a new language or environment. | ||
|
||
The objectives are simple: | ||
|
||
- Write a function that returns the string "Hello, World!". | ||
- Modify the provided code so that it produces the string "Hello, World!". | ||
- Run the test suite and make sure that it succeeds. | ||
- Submit your solution and check it at the website. | ||
|
||
If everything goes well, you will be ready to fetch your first real exercise. | ||
|
||
[hello-world]: https://en.wikipedia.org/wiki/%22Hello,_world!%22_program |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# This is an auto-generated file. | ||
# | ||
# Regenerating this file via `configlet sync` will: | ||
# - Recreate every `description` key/value pair | ||
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications | ||
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) | ||
# - Preserve any other key/value pair | ||
# | ||
# As user-added comments (using the # character) will be removed when this file | ||
# is regenerated, comments can be added via a `comment` key. | ||
|
||
[af9ffe10-dc13-42d8-a742-e7bdafac449d] | ||
description = "Say Hi!" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
// Version: 1.1.0 | ||
|
||
#include "vendor/unity.h" | ||
|
||
extern const char *hello(void); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
FUNC_PROTO = """\ | ||
#include "vendor/unity.h" | ||
extern const char *hello(void); | ||
""" | ||
|
||
def gen_func_body(prop, inp, expected): | ||
return f'TEST_ASSERT_EQUAL_STRING("{expected}", {prop}());\n' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#!/usr/bin/env python3 | ||
|
||
import argparse | ||
import importlib | ||
import json | ||
import re | ||
import subprocess | ||
import sys | ||
import textwrap | ||
import tomllib | ||
|
||
|
||
def camel_to_snake(s): | ||
s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", s) | ||
return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() | ||
|
||
|
||
def desc_to_funcname(desc): | ||
s = "test_" + desc.replace(" ", "_").lower() | ||
return re.sub("[^a-z0-9_]", "", s) | ||
|
||
|
||
def read_canonical_data(exercise): | ||
prefix = "Using cached 'problem-specifications' dir: " | ||
args = ['bin/configlet', 'info', '-o', '-v', 'd'] | ||
info = subprocess.run(args, capture_output=True, check=True, text=True).stdout.split("\n") | ||
cache_dir = [line[len(prefix):] for line in info if line.startswith(prefix)] | ||
if len(cache_dir) != 1: | ||
raise Exception("Could not determine 'problem-specifications' dir") | ||
path = f"{cache_dir[0]}/exercises/{exercise}/canonical-data.json" | ||
with open(path, "r") as f: | ||
return json.loads(f.read()) | ||
|
||
|
||
def flatten_cases(data): | ||
cases_by_id = {} | ||
|
||
def traverse(node): | ||
nonlocal cases_by_id | ||
if "cases" in node: | ||
for child in node["cases"]: | ||
traverse(child) | ||
else: | ||
add_case(cases_by_id, node) | ||
|
||
traverse(data) | ||
return [cases_by_id[uuid] for uuid in cases_by_id] | ||
|
||
|
||
def add_case(cases_by_id, case): | ||
if "reimplements" in case: | ||
cases_by_id[case["reimplements"]] = case | ||
else: | ||
cases_by_id[case["uuid"]] = case | ||
|
||
|
||
def gen_funcname(mod, case): | ||
if hasattr(mod, "describe"): | ||
description = mod.describe(case) | ||
else: | ||
description = case["description"] | ||
return desc_to_funcname(description) | ||
|
||
|
||
def gen_main(mod, cases): | ||
str_list = [] | ||
str_list.append("int main(void) {\n UNITY_BEGIN();\n") | ||
for case in cases: | ||
funcname = gen_funcname(mod, case) | ||
str_list.append(f" RUN_TEST({funcname});\n") | ||
str_list.append(" return UNITY_END();\n}\n") | ||
return "".join(str_list) | ||
|
||
|
||
def gen_test_case(mod, case, test_ignore): | ||
str_list = [] | ||
funcname = gen_funcname(mod, case) | ||
str_list.append(f"void {funcname}(void) {{\n") | ||
if test_ignore: | ||
str_list.append(" TEST_IGNORE();\n") | ||
prop = camel_to_snake(case["property"]) | ||
body = mod.gen_func_body(prop, case["input"], case["expected"]) | ||
body = textwrap.indent(body, " ") | ||
str_list.append(f"{body}}}\n\n") | ||
return "".join(str_list) | ||
|
||
|
||
def gen_test_cases(mod, cases): | ||
str_list = [] | ||
test_ignore = False | ||
for case in cases: | ||
str_list.append(gen_test_case(mod, case, test_ignore)) | ||
test_ignore = True | ||
return "".join(str_list) | ||
|
||
|
||
def gen_test_file(mod, cases): | ||
str_list = [] | ||
str_list.append(mod.FUNC_PROTO) | ||
str_list.append("\nvoid setUp(void) {\n}\n\nvoid tearDown(void) {\n}\n\n") | ||
str_list.append(gen_test_cases(mod, cases)) | ||
str_list.append(gen_main(mod, cases)) | ||
return "".join(str_list) | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser() | ||
parser.add_argument("exercise") | ||
parser.add_argument("-i", "--ignore-toml", action="store_true") | ||
args = parser.parse_args() | ||
data = read_canonical_data(args.exercise) | ||
cases = flatten_cases(data) | ||
if not args.ignore_toml: | ||
test_toml = f"{sys.path[0]}/../exercises/practice/{args.exercise}/.meta/tests.toml" | ||
with open(test_toml, "rb") as f: | ||
test_toml = tomllib.load(f) | ||
cases = list(filter(lambda case : test_toml.get(case['uuid'],{}).get('include', True), cases)) | ||
exercise = args.exercise.replace("-", "_") | ||
mod = importlib.import_module("exercises." + exercise) | ||
if hasattr(mod, "extra_cases"): | ||
cases = cases[:] | ||
cases.extend(mod.extra_cases()) | ||
s = gen_test_file(mod, cases) | ||
path = f"{sys.path[0]}/../exercises/practice/{args.exercise}/{exercise}_test.c" | ||
with open(path, "w") as f: | ||
f.write(s) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |