)]}'
{
  "log": [
    {
      "commit": "b05c53fb01342873787b545359f443b5dbf2be7f",
      "tree": "fb628f9aa5e539abc5bd2e64240bb0211707be0b",
      "parents": [
        "ad74392541b9a72b521e56673cfdd013532fbd4a"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:13:36 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:13:36 2026 +0400"
      },
      "message": "parser: implement recursive-descent parser with tests\n\n- Parse([]token.Token) (ast.Node, error) converts tokens to AST\n- Grammar: expr → term ((+|-) term)*, term → factor ((*|/) factor)*, factor → NUMBER | \u0027(\u0027 expr \u0027)\u0027\n- Correct operator precedence (* / before + -)\n- Left-associative operators\n- Error handling: empty input, missing/unexpected parens, trailing tokens, consecutive operators\n- 19 unit tests covering success and error cases\n"
    },
    {
      "commit": "ad74392541b9a72b521e56673cfdd013532fbd4a",
      "tree": "ee34b43689463e1ed0bc533aff7ca4ce6b2c0045",
      "parents": [
        "cdbb189191d122228d802b9964101a751dd30931"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:11:41 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:11:41 2026 +0400"
      },
      "message": "Add AST package: Node interface, NumberLit, BinaryExpr\n\n- Sealed Node interface with unexported marker method\n- NumberLit holds float64 value\n- BinaryExpr holds Op (token.Type), Left and Right children\n- Tests verify interface satisfaction, data integrity, and nesting\n"
    },
    {
      "commit": "cdbb189191d122228d802b9964101a751dd30931",
      "tree": "c1e1a9b48d7f8405e0fcb0a15e3285cf7b38e404",
      "parents": [
        "d04f362cd432ad643a69ca89f4aba57987b87170"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:10:35 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:10:35 2026 +0400"
      },
      "message": "Add lexer: Tokenize function with full test coverage\n\nImplements lexer.Tokenize(input string) ([]token.Token, error)\n- Skips whitespace\n- Parses integer and decimal numbers (including leading dot e.g. .5)\n- Handles all operators: + - * /\n- Handles parentheses: ( )\n- Appends EOF token\n- Returns error on invalid characters with position info\n- 12 unit tests covering: empty, whitespace-only, integers, decimals,\n  leading-dot numbers, operators, parens, full expressions, no-space\n  expressions, invalid chars, multiple decimals (1.2.3)\n"
    },
    {
      "commit": "d04f362cd432ad643a69ca89f4aba57987b87170",
      "tree": "34b53f6a319a30f893ce39fbe958e273cdb92a9a",
      "parents": [
        "719e823878d91176baf06c646ddec638530c10b8"
      ],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:09:06 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:09:06 2026 +0400"
      },
      "message": "token: add Type enum, Token struct, and String() method\n"
    },
    {
      "commit": "719e823878d91176baf06c646ddec638530c10b8",
      "tree": "4bec55aaaba03d29081524704af760d3ef370f9d",
      "parents": [],
      "author": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:07:50 2026 +0400"
      },
      "committer": {
        "name": "Sketch🕴️",
        "email": "skallywag@sketch.dev",
        "time": "Sat Feb 28 19:07:50 2026 +0400"
      },
      "message": "Step 1: project skeleton - go mod, directory structure, placeholder main.go\n"
    }
  ]
}
