// JSON Parser - Stress tests recursion, ADTs, and string manipulation // // This demonstrates: // - Recursive descent parsing // - Complex ADTs for AST representation // - Character handling with Char type // - Error handling with Result type // JSON Value representation type JsonValue = | JsonNull | JsonBool(Bool) | JsonNumber(Int) | JsonString(String) | JsonArray(List) // Parser state: input chars and position type ParseState = | ParseState(List, Int) // Parser result type ParseResult = | ParseOk(T, ParseState) | ParseErr(String) // Get current character fn peek(state: ParseState): Option = match state { ParseState(chars, pos) => List.get(chars, pos) } // Advance position fn advance(state: ParseState): ParseState = match state { ParseState(chars, pos) => ParseState(chars, pos + 1) } // Skip whitespace fn skipWhitespace(state: ParseState): ParseState = match peek(state) { None => state, Some(c) => if c == ' ' || c == '\t' || c == '\n' then skipWhitespace(advance(state)) else state } // Check if character is a digit fn isDigit(c: Char): Bool = c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || c == '6' || c == '7' || c == '8' || c == '9' fn digitValue(c: Char): Int = if c == '0' then 0 else if c == '1' then 1 else if c == '2' then 2 else if c == '3' then 3 else if c == '4' then 4 else if c == '5' then 5 else if c == '6' then 6 else if c == '7' then 7 else if c == '8' then 8 else if c == '9' then 9 else 0 // Parse a number fn parseNumber(state: ParseState): ParseResult = parseDigits(state, 0, false) fn parseDigits(state: ParseState, acc: Int, hasDigits: Bool): ParseResult = match peek(state) { None => if hasDigits then ParseOk(acc, state) else ParseErr("Expected digit"), Some(c) => if isDigit(c) then parseDigits(advance(state), acc * 10 + digitValue(c), true) else if hasDigits then ParseOk(acc, state) else ParseErr("Expected digit") } // Parse a string (simple version - no escapes) fn parseString(state: ParseState): ParseResult = match peek(state) { None => ParseErr("Unexpected end"), Some(c) => if c != '"' then ParseErr("Expected quote") else parseStringContent(advance(state), "") } fn charToString(c: Char): String = String.fromChar(c) fn parseStringContent(state: ParseState, acc: String): ParseResult = match peek(state) { None => ParseErr("Unterminated string"), Some(c) => if c == '"' then ParseOk(acc, advance(state)) else parseStringContent(advance(state), acc + charToString(c)) } // Check if next chars match a keyword fn matchKeyword(state: ParseState, keyword: String): Bool = matchKeywordChars(state, String.chars(keyword), 0) fn matchKeywordChars(state: ParseState, keywordChars: List, idx: Int): Bool = match List.get(keywordChars, idx) { None => true, Some(kc) => match state { ParseState(chars, pos) => match List.get(chars, pos + idx) { None => false, Some(ic) => if ic == kc then matchKeywordChars(state, keywordChars, idx + 1) else false } } } fn advanceBy(state: ParseState, n: Int): ParseState = if n <= 0 then state else advanceBy(advance(state), n - 1) // Main value parser fn parseValue(state: ParseState): ParseResult = { let s = skipWhitespace(state) match peek(s) { None => ParseErr("Unexpected end of input"), Some(c) => if matchKeyword(s, "null") then ParseOk(JsonNull, advanceBy(s, 4)) else if matchKeyword(s, "true") then ParseOk(JsonBool(true), advanceBy(s, 4)) else if matchKeyword(s, "false") then ParseOk(JsonBool(false), advanceBy(s, 5)) else if c == '"' then match parseString(s) { ParseErr(e) => ParseErr(e), ParseOk(str, newState) => ParseOk(JsonString(str), newState) } else if c == '[' then parseArray(s) else if isDigit(c) then match parseNumber(s) { ParseErr(e) => ParseErr(e), ParseOk(num, newState) => ParseOk(JsonNumber(num), newState) } else ParseErr("Unexpected character") } } // Parse array fn parseArray(state: ParseState): ParseResult = match peek(state) { None => ParseErr("Unexpected end"), Some(c) => if c != '[' then ParseErr("Expected [") else parseArrayElements(skipWhitespace(advance(state)), []) } fn parseArrayElements(state: ParseState, acc: List): ParseResult = match peek(state) { None => ParseErr("Unterminated array"), Some(c) => if c == ']' then ParseOk(JsonArray(acc), advance(state)) else match parseValue(state) { ParseErr(e) => ParseErr(e), ParseOk(value, newState) => { let afterComma = skipWhitespace(newState) match peek(afterComma) { None => ParseErr("Unterminated array"), Some(next) => if next == ']' then ParseOk(JsonArray(List.concat(acc, [value])), advance(afterComma)) else if next == ',' then parseArrayElements(skipWhitespace(advance(afterComma)), List.concat(acc, [value])) else ParseErr("Expected , or ]") } } } } // Public parse function fn parse(input: String): Result = match parseValue(ParseState(String.chars(input), 0)) { ParseErr(e) => Err(e), ParseOk(value, _) => Ok(value) } // Pretty print JSON fn valueToString(value: JsonValue): String = match value { JsonNull => "null", JsonBool(b) => if b then "true" else "false", JsonNumber(n) => toString(n), JsonString(s) => "\"" + s + "\"", JsonArray(items) => "[" + String.join(List.map(items, valueToString), ", ") + "]" } // Test the parser fn main(): Unit with {Console} = { Console.print("=== JSON Parser Test ===") Console.print("") Console.print("Test 1: Simple values") testParse("null") testParse("true") testParse("false") testParse("42") testParse("\"hello\"") Console.print("") Console.print("Test 2: Arrays") testParse("[]") testParse("[1, 2, 3]") testParse("[true, false, null]") testParse("[1, [2, 3], 4]") Console.print("") Console.print("Test 3: Nested arrays") testParse("[[1, 2], [3, 4]]") } fn testParse(input: String): Unit with {Console} = match parse(input) { Ok(value) => Console.print(" " + input + " => " + valueToString(value)), Err(e) => Console.print(" ERROR: " + e) } let output = run main() with {}