from lark import Lark, Transformer, v_args import json, sys, re """describe college courses, their number of units, any other courses that are prerequisites, as well as various degrees which consist of groups of courses that must be taken. The groups have rules associated with them, such as: - take all (the following courses) - take n (where n=3 or 5 or something) - take at least n units from the following list (so the taken course's unit value must add up to n or greater) - and so on.""" class Course: def __init__(self, name, units, prerequisites=[]): self.name = name self.units = units self.prerequisites = prerequisites def __repr__(self): return f"{self.name} ({self.units} units)" class DegreeRule: def __init__(self, rule_type, course_list): self.rule_type = rule_type if 'n' in rule_type.__dict__: self.n = rule_type.n self.course_list = course_list def __repr__(self): return f"DegreeRule({self.rule_type}, {self.course_list})" class Degree: def __init__(self, name, degree_rules): self.name = name self.degree_rules = degree_rules def __repr__(self): return f"Degree({self.name}, {self.degree_rules})" class RuleType(): def __init__(self,what,n=0): self.what = what self.n = n def __repr__(self): return "" #f"RuleType({self.what}, {self.n})" class Rule: pass class TakeAll(Rule): def __repr__(self): return f"Take all:" class TakeN(Rule): def __init__(self, n): self.n = n def __repr__(self): return f"Take {self.n} courses:" class TakeNUnits(Rule): def __init__(self, n): self.n = n def __repr__(self): return f"Take {self.n} units:" @v_args(inline=True) class DSLTransformer(Transformer): def __init__(self): self.courses = {} self.degrees = {} def course_declaration(self, name, units): #return Course(name, int(units), prerequisites.children) self.courses[name.value] = Course(name.value, int(units)) '''def program(self, name, rules): self.degrees[name.value] = Degree(name.value, rules) return self.degrees[name.value]''' def rule_type(self, items): what = items[0] n = None if len(items) == 2: n = int(items[1]) return RuleType(what, n) def take_all(self): return TakeAll() def take_n_courses(self, n): return TakeN(n) def take_n_units(self, n): return TakeNUnits(n) def degree_rule(self, rule_type, course_list): return DegreeRule(rule_type, course_list) def course_list(self, *courses): return [c.value for c in courses] def program(self, *items): #print(items) name = items[0].value rules = items[1:] self.degrees[name] = Degree(name, rules) return self.degrees[name] """ dsl = DSLTransformer() dsl.course('CS101', 3) dsl.course('CS102', 3, prerequisites=['CS101']) dsl.course('MATH101', 3) dsl.degree('CS', [ dsl.take_all(['CS101', 'CS102']), dsl.take_n_units(['CS101', 'CS102', 'MATH101'], 9), dsl.take_n(['CS101', 'CS102', 'MATH101'], 2), ]) print(dsl.courses) print(dsl.degrees) """ grammar = """ start: _spec+ _spec: program | course_declaration program: "program" PROGRAMNAME degree_rule* degree_rule: "take" rule_type course_list rule_type: "all from" -> take_all | "at least" INT "units from" -> take_n_units | "at least" INT "courses from" -> take_n_courses course_declaration: "course" COURSECODE INT "units" ["prerequisites" (COURSECODE ","?)*] course_list: (COURSECODE ","?)* COURSECODE: ("A".."Z")+ INT+ ["A".."Z"]* PROGRAMNAME: ("A".."Z" | "a".."z")+ %import common.INT %import common.WS %ignore WS """ def parser(): dsl = """ course CSIS1 2 units course CSIS2 4 units course ACCT120 3 units course ACCT20 4 units course BUS1 3 units course ENGL250 3 units course ACCT105 3 units course ACCT121 3 units course BOT100 3 units course BOT105B 3 units program BusinessAccountingOption take all from CSIS1, CSIS2 take at least 6 units from ACCT120, ACCT20 take at least 3 courses from BUS1, ENGL250, ACCT120, BOT100, ACCT121, BOT105B """ parser = Lark(grammar) print(parser.parse(dsl).pretty()) print("\n\n\n") parser = Lark(grammar) transformer = DSLTransformer() def parse_dsl(dsl): tree = parser.parse(dsl) return transformer.transform(tree) result = parse_dsl(dsl) print(transformer.courses) print() print(transformer.degrees) print() [print(c) for c in transformer.courses] print() [print(d) for d in transformer.degrees] def rule_check_units(): # Define the course units and passing grades course_units = { "A": 1, "B": 2, "C": 1, "D": 3 } # Define the students and their passed courses students = { "Peter": ["A", "B", "C"], "Mark": ["D", "B"], "Susan": ["A", "B"] } # Define the minimum units required to graduate min_units_to_graduate = 4 calcs = {} for student, passed_courses in students.items(): print("\n", student, passed_courses) for c in passed_courses: print(f"\t{c:15} {course_units[c]}") print(f"\t{'Total':15} {sum(course_units[c] for c in passed_courses)}") # Determine the graduating students graduating_students = [ student for student, passed_courses in students.items() if sum(course_units[course] for course in passed_courses) >= min_units_to_graduate ] # Print the graduating students if graduating_students: print("The graduating students are:", ", ".join(graduating_students)) else: print("No students meet the graduation requirements.") """ def compute_degree_progress(student_courses, degree): total_units_completed = 0 total_required_units = 0 for course in degree['required_courses']: if course['id'] in student_courses: total_units_completed += course['units'] if 'additional_rules' in course: for rule in course['additional_rules']: if rule['type'] == 'at_least_n_units_from_list': units_from_list = sum([c['units'] for c in degree['courses'] if c['id'] in student_courses and c['id'] in rule['course_list']]) if units_from_list < rule['n']: break total_required_units += course['units'] progress_percent = total_units_completed / total_required_units * 100 if total_required_units > 0 else 0 return progress_percent """ if __name__ == "__main__": options = { 1: ['parsing example',parser] , 2: ['rule check by units',rule_check_units] , } print ('') if len(sys.argv) > 1 and re.search(r'^\d+',sys.argv[1]): resp = int(sys.argv[1]) print("\n\nPerforming: %s\n\n" % options[resp][0]) else: print ('') for key in options: print(str(key) + '.\t' + options[key][0]) print('') resp = input('Choose: ') # Call the function in the options dict options[ int(resp)][1]()