first draft degree parser

This commit is contained in:
Coding with Peter 2023-05-18 17:50:31 -07:00
parent 27d05f0712
commit 484cc4235f
1 changed files with 124 additions and 68 deletions

View File

@ -1,5 +1,5 @@
from lark import Lark, Transformer, v_args
import json
@ -13,57 +13,57 @@ consist of groups of courses that must be taken. The groups have rules associate
grammar = """
start: program
program: course_declaration*
course_declaration: "course" CNAME INT "prerequisites" (CNAME ","?)* "degree" CNAME "{" degree_rule* "}"
degree_rule: "take" ("all" | "at" INT "from") ("the" "following") "courses" course_list
course_list: (CNAME ","?)*
%import common.CNAME
%import common.INT
%import common.WS
%ignore WS
"""
class Course:
def __init__(self, name, units, prerequisites, degree):
def __init__(self, name, units, prerequisites=[]):
self.name = name
self.units = units
self.prerequisites = prerequisites
self.degree = degree
def __repr__(self):
return f"{self.name} ({self.units} units)"
class DegreeRule:
def __init__(self, verb, n, course_list):
self.verb = verb
self.n = n
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 __init__(self, courses):
self.courses = courses
def __repr__(self):
return f"Take all:"
class TakeN(Rule):
def __init__(self, courses, n):
self.courses = courses
def __init__(self, n):
self.n = n
def __repr__(self):
return f"Take {self.n} courses:"
class TakeNUnits(Rule):
def __init__(self, courses, n):
self.courses = courses
def __init__(self, n):
self.n = n
def __repr__(self):
return f"Take {self.n} units:"
@v_args(inline=True)
class DSLTransformer(Transformer):
@ -71,38 +71,44 @@ class DSLTransformer(Transformer):
self.courses = {}
self.degrees = {}
def course_declaration(self, name, units, prerequisites, degree):
return Course(name, int(units), prerequisites.children, degree)
def course_declaration(self, name, units):
#return Course(name, int(units), prerequisites.children)
self.courses[name.value] = Course(name.value, int(units))
def degree(self, name, required_courses):
self.degrees[name] = Degree(name, required_courses)
'''def program(self, name, rules):
self.degrees[name.value] = Degree(name.value, rules)
return self.degrees[name.value]'''
def take_all(self, courses):
return TakeAll(courses)
def take_n(self, courses, n):
return TakeN(courses, n)
def take_n_units(self, courses, n):
return TakeNUnits(courses, n)
def degree_rule(self, items):
verb = items[0]
def rule_type(self, items):
what = items[0]
n = None
course_list = items[-1]
if len(items) == 4:
n = int(items[2])
return DegreeRule(verb, n, course_list.children)
if len(items) == 2:
n = int(items[1])
return RuleType(what, n)
def take_all(self):
return TakeAll()
def course_list(self, items):
return items[:-1]
def take_n_courses(self, n):
return TakeN(n)
def program(self, items):
courses = [item for item in items if isinstance(item, Course)]
degrees = [item for item in items if isinstance(item, Degree)]
return courses, degrees
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'])
@ -115,11 +121,65 @@ dsl.degree('CS', [
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
"""
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()
@ -127,24 +187,19 @@ def parse_dsl(dsl):
tree = parser.parse(dsl)
return transformer.transform(tree)
dsl = """
course CS101 3 prerequisites none degree CS {
take all the following courses {
CS102, CS103
}
take at least 6 from {
MATH101, MATH102, MATH103
}
}
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]
"""
courses, degrees = parse_dsl(dsl)
print(courses)
print(degrees)
def compute_degree_progress(student_courses, degree):
total_units_completed = 0
@ -162,3 +217,4 @@ def compute_degree_progress(student_courses, degree):
progress_percent = total_units_completed / total_required_units * 100 if total_required_units > 0 else 0
return progress_percent
"""