Inspiration

Every student has typed their grades into an online calculator at 11pm the night before a final, trying to answer one question: "what do I need to get an A?" And every single one of those calculators makes you start from scratch — re-type every category, every weight, every score — because none of them actually remember your class. We wanted a grade calculator that worked the way a real class syllabus does: define the policy once, and have everything else — the calculator, the syllabus wording, even a spreadsheet formula — fall out of that one definition automatically. This Project taught me so much as it was my first main piece of coding work.

What it does

GradeScript is a tiny programming language for grading policies. You write something like: class Biology { category Homework = 15% category Tests = 40% category Labs = 25% category Final = 20%

late Homework: max 70% retake Tests: max 80% }

and it becomes:

  • A live grade calculator — type your current average into each category and watch your overall grade update instantly, with best-case and worst-case projections for anything you haven't started yet.
  • A reverse solver — instead of only going forward (enter grades, get an average), you can ask "what do I need on the Final to get a 90%?" and it solves backward for the exact minimum score. Almost no calculator we could find does this.
  • A GPA Dashboard — most calculators only ever show one class. Ours shows every class you've built at once, on the standard 4.0 scale.
  • A syllabus paragraph, a spreadsheet formula, and an LMS config — the same policy, auto-generated into three more formats a teacher or student would actually use. The weighted-grade math underneath is simple but real: $$\text{grade} = \frac{\sum_i w_i \cdot a_i}{\sum_i w_i} \times 100$$ where $w_i$ is each category's weight and $a_i$ is your average in it — and the reverse solver just runs that equation backward for one unknown category. ## How we built it Under the hood it's a real compiler pipeline, not a template engine: a hand-written lexer tokenizes the source, a recursive-descent parser builds an AST out of it, and from that single AST we walk it four different ways — once to compute a live grade, once to solve the reverse-target equation, and three more times to generate the syllabus text, the spreadsheet formula, and the LMS config. Because every output comes from the same parsed tree, none of them can quietly disagree with each other. On top of the language, we built a table-based editor so nobody has to type raw syntax to use it — category name, weight, late cap, retake cap, all as simple form fields that generate real GradeScript source behind the scenes (viewable read-only if you're curious). Every class you build is tracked by a stable identity separate from its display name, and everything persists to the browser's local storage automatically. ## Challenges we ran into The trickiest bugs weren't in the language itself — the lexer/parser/ interpreter pipeline was actually the most straightforward part. The real challenges were in the interactive UI:
  • A focus-loss bug: early on, every keystroke in the table editor was re-rendering the entire table, which destroyed and recreated the input you were actively typing in — kicking your cursor out mid-word. The fix was splitting rendering into two tiers: a full rebuild only for structural changes (adding/removing a category), and a lightweight, targeted update for value edits that never touches the input you're typing in.
  • A rename bug that caused real data loss: we originally tracked each class by its class name. Renaming a class deleted its saved data under the old name and re-saved it under the new one — which meant switching away and back reverted the original to defaults and left an orphaned duplicate sitting in the GPA Dashboard. We fixed it by giving every class a stable "slot" identity completely decoupled from its (mutable) display name, so a rename can never lose or duplicate data again.
  • A layout bug masquerading as "too much padding": we spent a while trimming CSS spacing to stop a panel from needing to scroll, before realizing the actual cause was that the CSS grid holding the panels never had grid-template-rows set — so panels were sizing to fit their content instead of stretching to fill the available height. One line fixed what padding tweaks alone couldn't. ## Accomplishments that we're proud of Getting the reverse solver's math right and demonstrably correct — we hand-verified it against manually-calculated grades at every step, not just trusting the code. And building a genuinely dependency-free project: no build step, no npm install, no framework — just open one HTML file and it works. ## What we learned That the hardest bugs in an interactive tool are rarely in the "smart" part (the compiler) — they're in the boring-sounding parts, like DOM re-rendering and CSS grid sizing, that quietly break the experience in ways that are easy to miss until someone actually uses it the way a real person would. ## What's next for GradeScript Linking the GPA Dashboard to every class you've edited live, not just whichever one is currently open. Extending the reverse solver to handle multiple still-ungraded categories at once, not just one at a time. And more export targets — a PowerSchool-style format, or a Notion database template — since adding one is just one more function against the existing parsed structure, no language changes required.
Share this project:

Updates

Submission history