Skip to content

Latest commit

 

History

History
87 lines (66 loc) · 7.44 KB

File metadata and controls

87 lines (66 loc) · 7.44 KB

Repository Guidelines

Project Overview

json-parser is a from-scratch JSON parser implementation in Java, built as part of the codingchallenges.fyi "Build Your Own JSON Parser" challenge. It is a Maven single-module project (org.example:json-parser:1.0-SNAPSHOT) with no external runtime dependencies — only JUnit for testing. The project is early-stage: the lexer is partially implemented, the parser is an empty stub, and the public entry point has not been built yet.

Architecture & Data Flow

Intended pipeline (recursive-descent style JSON parsing):

source text → PushbackReader → Lexer.nextToken() → Token(TokenType, lexeme, line, column) → Parser → parsed value → JsonParser (entry point)
  • Lexer (src/main/java/org/example/jsonparser/Lexer.java) wraps a java.io.PushbackReader and exposes Token nextToken() throws IOException. It tracks line/column for error reporting and has private helpers advance(), skipWhiteSpace(), readString(), readNumber(), readKeyword().
  • Token is an immutable record Token(TokenType type, String lexeme, int line, int column).
  • TokenType is a flat enum: LBRACE, RBRACE, COLON, COMMA, STRING, NUMBER, TRUE, FALSE, NULL, EOF. Note: no LBRACKET/RBRACKET yet — JSON array syntax ([, ]) is not tokenized.
  • Parser (src/main/java/org/example/jsonparser/Parser.java) is currently an empty class (public class Parser {}). No recursive-descent logic, no result type defined yet.
  • JsonParser (src/main/java/org/example/jsonparser/JsonParser.java) is the intended public entry point; currently main() just prints a placeholder ("Step 0" stub). No parse(...) API exists yet.
  • JsonParseException (unchecked, extends RuntimeException) carries line/column and is thrown from Lexer.readString() (unterminated string) and Lexer.readKeyword() (unrecognized keyword). Parser does not yet throw/handle errors.

Known bugs in Lexer to be aware of when extending it (do not silently "fix" without checking test expectations first):

  • skipWhiteSpace() condition is inverted (!Character.isWhitespace(...)) — currently skips non-whitespace instead of whitespace.
  • '{'/'}' are mapped to RBRACE/LBRACE respectively (swapped).
  • No initial advance() call in the constructor, so currentChar starts at 0 before the first nextToken() call.
  • The default case in nextToken()'s dispatch returns new Token(null, null, 0, 0) for unrecognized characters instead of throwing JsonParseException.
  • readNumber() doesn't handle exponents (e/E) after a decimal point.

Key Directories

Path Purpose
src/main/java/org/example/jsonparser/ All production code (single flat package, no sub-packages): Lexer.java, Token.java, TokenType.java, Parser.java, JsonParser.java, JsonParseException.java
src/main/resources/ Empty — no config, fixtures, or schemas yet
src/test/java/org/example/jsonparser/ JUnit 5 tests, mirrors the main package
.mvn/ Present but empty — no Maven wrapper committed
target/ Maven build output (gitignored); regenerated by mvn

Development Commands

No Maven wrapper is committed — mvn must be on PATH.

mvn clean test      # compile + run all tests (JUnit via Surefire)
mvn compile          # compile main sources only
mvn package          # build target/json-parser-1.0-SNAPSHOT.jar
mvn -Dtest=SanityTest test   # run a single test class

There is no linter, formatter, or CI configuration in this repo — none to run.

Code Conventions & Common Patterns

  • Java 24, compiled with maven.compiler.source/target/release=24, UTF-8 source encoding. Preview features and annotation processing are disabled.
  • Single flat package: everything lives in org.example.jsonparser — no sub-packaging by layer.
  • Records for data: Token is a record; use records for new immutable data carriers rather than plain classes with boilerplate getters.
  • Enums for closed sets: TokenType is a plain enum with no fields/methods — follow this for token/kind classification.
  • Switch expressions: Lexer.readKeyword() uses arrow-form switch expressions (return switch (lexeme) { case "true" -> ...; }) — prefer this modern form over classic switch statements for new dispatch logic.
  • Exceptions: JsonParseException extends RuntimeException (unchecked) and always carries line/column for diagnostics; the message is built as message + " at line " + line + ", column " + column. Follow this pattern for any new parse-time errors instead of throwing generic exceptions.
  • I/O: Lexer is built around PushbackReader for single-character lookahead/pushback rather than buffering the whole input into a String — preserve this streaming style if extending the lexer.
  • No javadoc/comments anywhere in the current codebase — match the terse style unless documenting genuinely non-obvious logic.
  • No external libraries beyond JDK stdlib in main code; JUnit Jupiter is test-scope only.

Important Files

  • src/main/java/org/example/jsonparser/JsonParser.java — intended CLI/API entry point (currently a stub main)
  • src/main/java/org/example/jsonparser/Lexer.java — tokenizer; most substantial logic currently in the project
  • src/main/java/org/example/jsonparser/Parser.java — recursive-descent parser to be implemented (currently empty)
  • src/main/java/org/example/jsonparser/Token.java / TokenType.java — token model
  • src/main/java/org/example/jsonparser/JsonParseException.java — error type with line/column context
  • pom.xml — single source of build truth (Java version, dependencies, plugins)

Runtime/Tooling Preferences

  • Build tool: Maven (system-installed mvn; no wrapper committed, though .gitignore un-ignores .mvn/wrapper/maven-wrapper.jar suggesting one was once intended).
  • JDK: 24 (openjdk-24 per .idea/misc.xml; matches Eclipse .settings/org.eclipse.jdt.core.prefs).
  • IDE metadata: Both IntelliJ (.idea/) and Eclipse (.classpath, .project, .settings/) config exist locally but are gitignored — don't rely on them being present in a fresh checkout.
  • No package manager beyond Maven Central — the only dependency is org.junit.jupiter:junit-jupiter:6.0.1 (test scope).

Testing & QA

  • Framework: JUnit 5 (Jupiter), run via maven-surefire-plugin 3.5.4. Uses @Test and org.junit.jupiter.api.Assertions.assertEquals — no @ParameterizedTest, AssertJ, or Hamcrest currently in use.
  • Current state: src/test/java/org/example/jsonparser/SanityTest.java contains exactly one test, junitIsRunning(), which only asserts 2 == 1 + 1 to confirm the test harness is wired up. No parser functionality (Lexer, Parser, JsonParser, JsonParseException) is tested yet.
  • No test resources: src/test/resources/ does not exist; no JSON fixture files anywhere.
  • When adding tests: follow the existing camelCase, descriptive-phrase method naming (e.g. junitIsRunning), place them under src/test/java/org/example/jsonparser/, and prefer covering the Lexer first (valid tokens, whitespace, strings with escapes, numbers, keywords, unterminated-string errors) since it's the most implemented component. Given the identified Lexer bugs above, confirm expected behavior with the user/spec before writing tests that assume current (buggy) behavior is correct.
Write synthesized AGENTS.md to project root