Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Countdown

T–120 minutes to launch.

Mission Control has rejected the mission configuration. Implement the MON compiler before the countdown reaches zero.

Mission Control uses MON (Mission Object Notation) to exchange spacecraft configuration data between independent flight systems — guidance, propulsion, life support, and ground control all read and write the same .mon files. Before a mission can be launched, every configuration file must be validated and converted into a deterministic canonical form, so that cryptographic signatures computed over the file stay consistent no matter which system produced it or in what order its fields were written. A reordered key, a stray space, or an inconsistently formatted integer would break the signature — and a broken signature holds the countdown.

Your task is to implement the MON parser and canonicalizer used by Mission Control: a program that reads a .mon file, validates it against the specification below, and re-emits it in canonical form — or reports exactly what's wrong with it, so the file can be fixed before it reaches the pad.

There is no library for this. You write the parser.


1. The Format

A .mon file describes exactly one mission object: a flat, unordered set of unique key = value pairs, wrapped in a mission { ... } block. No nesting, no arrays, no comments.

mission {
    commander = @alice;
    emergency = no;
    fuel = 4200;
    name = "Apollo-X"
}

Your program must:

  1. Parse the source text against the grammar in §3.
  2. Validate it against every rule in §2 and §4.
  3. On success, print the object in canonical form (§4).
  4. On failure, print a single Error: ... line and exit non-zero — never an unhandled exception.

2. Specification

2.1 Keys

  • Lowercase ASCII letters only, az.
  • Unique within the object. Duplicate keys are an error — report it at the second (repeated) occurrence, not the first.
Example Valid?
commander
fuel
Commander ❌ uppercase
launch_code ❌ underscore
drone1 ❌ digit

2.2 Values

Exactly four value types are supported.

Type Syntax Examples
Integer 0 or a non-zero digit followed by digits 0, 7, 4200, 88451
String double-quoted, single line "Apollo-X"
Boolean one of two literal keywords yes, no
Crew identifier @ followed by lowercase letters @alice, @bob

Integers — positive or zero, no leading zeros. A single underscore _ may appear between two digits as a separator, purely for human readability in the source — it carries no meaning and is stripped when the value is canonicalized (§4).

Example Valid?
0
19
4200
4_200 ✅ digit separator
88_451_200 ✅ digit separator
01 ❌ leading zero
0042 ❌ leading zero
_400 ❌ leading underscore
400_ ❌ trailing underscore
4__200 ❌ consecutive underscores
0_400 0 cannot take a separator

Tokenization rule: an integer token is the maximal contiguous run of digits and underscores ([0-9_]+) — read the whole run before judging it, don't stop at the first offending character. Validate that full run against the integer production in §3; if it doesn't match, report Error: Invalid integer "<run>" (e.g. Error: Invalid integer "400_"), not an unrelated "unexpected character" error.

Strings — double-quoted, single line only. A newline before the closing quote is an error.

Booleans — exactly yes or no. No true/false, no other casing.

Crew identifiers@ immediately followed by one or more lowercase letters. No digits, no uppercase.

2.3 Object Format

mission {
key = value;
key2 = value;
key3 = value
}
  • The object begins with the keyword mission followed by {, and ends with }.
  • Entries are separated by ;.
  • There is no trailing semicolon after the last entry.
  • Whitespace (spaces, tabs, newlines) is insignificant and may appear anywhere between tokens.
  • Comments are not supported.
  • The empty mission mission {} is valid.

3. Grammar (EBNF)

mission    = "mission" "{" [ member { ";" member } ] "}" ;
member     = key "=" value ;

key        = lowercase { lowercase } ;

value      = integer | string | boolean | crewid ;

integer    = "0" | nonzero-digit { integer-tail } ;
integer-tail = digit | "_" digit ;
string     = '"' { any-char-except-quote-or-newline } '"' ;
boolean    = "yes" | "no" ;
crewid     = "@" lowercase { lowercase } ;

lowercase     = "a" | "b" | ... | "z" ;
digit         = "0" | "1" | ... | "9" ;
nonzero-digit = "1" | "2" | ... | "9" ;

4. Canonical Output

Given any valid mission object, print it back out as follows:

  • Keys sorted alphabetically.
  • Integer digit separators (_) are removed — canonical integers are a plain digit sequence, e.g. 4_200 canonicalizes to 4200.
  • Exactly one space on each side of =.
  • One entry per line, indented 4 spaces.
  • Semicolons between entries, omitted after the final entry — the canonical form keeps the same separator style as the source.
  • mission { and } each on their own line.
  • The empty mission canonicalizes to mission {} on a single line.

Input:

mission {
    fuel = 4200;
    name = "Apollo-X";
    commander = @alice;
    emergency = no
}

Canonical output:

mission {
    commander = @alice;
    emergency = no;
    fuel = 4200;
    name = "Apollo-X"
}

Digit separators are stripped, not preserved:

Input:

mission {
    launchcode = 88_451_200
}

Canonical output:

mission {
    launchcode = 88451200
}

5. Error Handling

Malformed input must produce a clear Error: <description> line — never a stack trace. At minimum, detect and report:

  • Unexpected / unrecognized character
  • Missing = in a member
  • Invalid integer (leading zeros; misplaced, trailing, leading, or consecutive digit-separator underscores)
  • Invalid key (uppercase, digits, underscores)
  • Duplicate key
  • Trailing semicolon before }
  • Missing { or }
  • Unterminated string

Examples:

Error: Duplicate key "commander"
Error: Invalid integer "00451"

The remaining cases just need to follow the Error: <description> shape and clearly identify the problem. Sample failures, each with its expected output:

Input Expected error
mission { name = "Sam" # } Error: Unexpected character '#' ...
mission { name "Sam" } Error: Expected '=' after key "name" ...
mission { name = "Sam } Error: Unterminated string ...
mission { name = "Sam"; } Error: Trailing semicolon before '}' is not allowed
mission { name = "Sam" (no closing brace) Error: Expected '}' to close mission object ...

6. Constraints

  • Python 3, standard library only.
  • Parser generators are forbidden (no ANTLR, Lark, PLY, etc.).
  • Parsing libraries are forbidden (no json, yaml, toml, pyparsing, etc. — even though MON is simple enough that json couldn't parse it anyway).
  • The implementation must be written by hand.

Beyond that, any architecture is acceptable as long as it satisfies the specification. Tokenizer + recursive-descent parser, a single-pass state machine, whatever you're most comfortable with.

Your entry point is a single function:

def canonicalize(source: str) -> str:
    ...

which takes raw .mon source text and returns its canonical form, raising MissionSyntaxError (with a message following §5) on invalid input.


7. Files

File Purpose
README.md This document
mon_simple.py Starter — implement canonicalize here
input1.mon, input2.mon Valid sample missions
expected1.mon, expected2.mon Exact expected canonical output
invalid1.mon Invalid sample — duplicate key
invalid2.mon Invalid sample — leading-zero integer

7.1 input1.monexpected1.mon

mission {
    fuel = 4200;
    name = "Apollo-X";
    commander = @alice;
    emergency = no
}
mission {
    commander = @alice;
    emergency = no;
    fuel = 4200;
    name = "Apollo-X"
}

7.2 input2.monexpected2.mon

mission {
  pilot=@bob;
    destination = "Europa Station";
  droneid = 7;
  emergency=yes;
    launchcode = 88451
}
mission {
    destination = "Europa Station";
    droneid = 7;
    emergency = yes;
    launchcode = 88451;
    pilot = @bob
}

7.3 invalid1.mon — duplicate key

mission {
    commander = @alice;
    pilot = @bob;
    commander = @carol
}

Expected: Error: Duplicate key "commander"

7.4 invalid2.mon — invalid integer

mission {
    launchcode = 00451;
    pilot = @bob
}

Expected: Error: Invalid integer "00451"


8. Evaluation

Submissions are scored against the sample files above and a held-out suite of additional hidden test cases covering the same rules — further key/value edge cases, additional malformed inputs, and boundary conditions (e.g. very long integers, empty strings, whitespace placement). Passing the samples in this repository is necessary but not sufficient for full marks.

Category Points
Correct parsing of all four value types 20
Correct canonical output (samples + hidden tests) 25
Correct error detection and messages (samples + hidden tests) 25
Edge cases (empty mission, 0, single-entry object, etc.) 10
First/Second/Third Submission 15/10/5
Code quality: clean, readable, sensibly organized 5
Total 100

9. Bonus (+15)

Extend your implementation to support nested mission blocks as a value type — i.e. a value may itself be a mission { ... } block — without changing the public interface (canonicalize(source: str) -> str keeps its signature, and flat missions like the samples above must still work exactly as before).

This is not required for full marks and is scored separately from the core rubric.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages