{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://www.treeova.com/whitepapers/treescript-language-schema.json",
  "title": "TreeScript Language Schema",
  "description": "Machine-readable contract for TreeScript v1 — covers source-level grammar surface, IR shape, stdlib registry, and authoring constraints. Companion to docs/whitepapers/treescript-language-reference.md.",
  "version": "1.0.0",
  "x-irVersion": "1.0.0",
  "x-compilerVersion": "1.0.0",

  "x-grammar": {
    "programOrder": [
      "study (exactly 1, first)",
      "input* (0..64)",
      "let* (0..512)",
      "emit* (plot|signal|alert, total 0..64)"
    ],
    "reservedKeywords": [
      "study","input","let","and","or","not","true","false",
      "int","float","bool","color","string","series",
      "overlay","min","max","default",
      "plot","signal","alert","title","severity","pane","message"
    ],
    "operatorPrecedence": [
      { "level": 1,  "ops": ["?:"],                                        "assoc": "right" },
      { "level": 2,  "ops": ["or"],                                         "assoc": "left"  },
      { "level": 3,  "ops": ["and"],                                        "assoc": "left"  },
      { "level": 4,  "ops": ["not"],                                        "assoc": "unary" },
      { "level": 5,  "ops": ["==","!=",">","<",">=","<="],                  "assoc": "left"  },
      { "level": 6,  "ops": ["+","-"],                                       "assoc": "left"  },
      { "level": 7,  "ops": ["*","/","%"],                                   "assoc": "left"  },
      { "level": 8,  "ops": ["unary -"],                                     "assoc": "unary" },
      { "level": 9,  "ops": ["expr[N] (history)","expr.field (record_get)"], "assoc": "left"  },
      { "level": 10, "ops": ["literal","ident","call","(expr)"],             "assoc": "n/a"   }
    ],
    "literals": {
      "number": { "pattern": "^[0-9]+(\\.[0-9]+)?$", "notes": "No scientific notation, no hex." },
      "string": { "delimiter": "\"", "escapes": ["\\n","\\t","\\\""] },
      "color":  { "pattern": "^#[0-9a-fA-F]{6}$", "notes": "7-char #RRGGBB only — no 3-digit form." },
      "bool":   { "values": ["true","false"] }
    },
    "comments": ["// line", "/* block */ (block may span lines)"],
    "history": {
      "syntax": "expr[N]",
      "constraint": "N is an integer literal in [1, 5000]"
    },
    "memberAccess": {
      "syntax": "expr.field",
      "appliesTo": "stdlib_call nodes whose returnType is record:*",
      "constraint": "field must match one of the record's declared keys"
    }
  },

  "x-types": {
    "scalars": ["int","float","bool","color","string"],
    "series":  ["series_float","series_bool"],
    "promotion": [
      "int + float = float",
      "scalar (op) series_float = series_float",
      "comparisons lift to series_bool when either side is a series",
      "and/or/not preserve bool vs series_bool",
      "scalar ternary requires bool cond; for per-bar branches use ta.iif"
    ]
  },

  "x-builtinSeries": [
    { "id": "open",      "type": "series_float", "desc": "Bar open" },
    { "id": "high",      "type": "series_float", "desc": "Bar high" },
    { "id": "low",       "type": "series_float", "desc": "Bar low" },
    { "id": "close",     "type": "series_float", "desc": "Bar close" },
    { "id": "volume",    "type": "series_float", "desc": "Bar volume" },
    { "id": "hl2",       "type": "series_float", "desc": "(high + low) / 2" },
    { "id": "hlc3",      "type": "series_float", "desc": "(high + low + close) / 3" },
    { "id": "ohlc4",     "type": "series_float", "desc": "(open + high + low + close) / 4" },
    { "id": "bar_index", "type": "int",          "desc": "Zero-based bar index" },
    { "id": "time",      "type": "series_float", "desc": "Bar timestamp (UTC seconds)" }
  ],

  "x-stdlib": [
    { "fn": "ta.sma",        "category": "moving_average", "params": [["src","series_float"],["len","int (literal)"]],                                            "returnType": "series_float" },
    { "fn": "ta.ema",        "category": "moving_average", "params": [["src","series_float"],["len","int (literal)"]],                                            "returnType": "series_float" },
    { "fn": "ta.rma",        "category": "moving_average", "params": [["src","series_float"],["len","int (literal)"]],                                            "returnType": "series_float" },
    { "fn": "ta.rsi",        "category": "momentum",       "params": [["src","series_float"],["len","int (literal)"]],                                            "returnType": "series_float" },
    { "fn": "ta.macd",       "category": "momentum",       "params": [["src","series_float"],["fast","int (literal)"],["slow","int (literal)"],["signal","int (literal)"]], "returnType": "record:macd,signal,hist" },
    { "fn": "ta.stoch",      "category": "momentum",       "params": [["len","int (literal)"],["smooth_k","int (literal)"],["smooth_d","int (literal)"]],         "returnType": "record:k,d" },
    { "fn": "ta.atr",        "category": "volatility",     "params": [["len","int (literal)"]],                                                                    "returnType": "series_float" },
    { "fn": "ta.bbands",     "category": "volatility",     "params": [["src","series_float"],["len","int (literal)"],["mult","float (literal)"]],                  "returnType": "record:upper,middle,lower" },
    { "fn": "ta.highest",    "category": "trend",          "params": [["src","series_float"],["len","int (literal)"]],                                            "returnType": "series_float" },
    { "fn": "ta.lowest",     "category": "trend",          "params": [["src","series_float"],["len","int (literal)"]],                                            "returnType": "series_float" },
    { "fn": "ta.crossover",  "category": "comparison",     "params": [["a","series_float"],["b","series_float"]],                                                  "returnType": "series_bool" },
    { "fn": "ta.crossunder", "category": "comparison",     "params": [["a","series_float"],["b","series_float"]],                                                  "returnType": "series_bool" },
    { "fn": "ta.iif",        "category": "utility",        "params": [["cond","series_bool"],["a","series_float"],["b","series_float"]],                           "returnType": "series_float" },
    { "fn": "math.abs",      "category": "math",           "params": [["x","any_scalar"]],                                                                         "returnType": "float" },
    { "fn": "math.max",      "category": "math",           "params": [["a","any_scalar"],["b","any_scalar"]],                                                      "returnType": "float" },
    { "fn": "math.min",      "category": "math",           "params": [["a","any_scalar"],["b","any_scalar"]],                                                      "returnType": "float" },
    { "fn": "math.round",    "category": "math",           "params": [["x","any_scalar"]],                                                                         "returnType": "float" },
    { "fn": "math.log",      "category": "math",           "params": [["x","any_scalar"]],                                                                         "returnType": "float" },
    { "fn": "math.sqrt",     "category": "math",           "params": [["x","any_scalar"]],                                                                         "returnType": "float" }
  ],

  "x-resourceLimits": {
    "MAX_SOURCE_BYTES":      200000,
    "MAX_INPUTS":            64,
    "MAX_LETS":              512,
    "MAX_EMITS":             64,
    "MAX_NODES":             2000,
    "MAX_HISTORY_LOOKBACK":  5000,
    "WORKER_HEARTBEAT_MS":   250,
    "WORKER_WATCHDOG_MS":    2000
  },

  "x-errorCodes": [
    "lex_error","parse_error","validation_error","type_error","scope_error",
    "history_bounds_error","resource_bounds_error","stdlib_unknown","stdlib_arity_error",
    "ir_version_mismatch","runtime_error","budget_exceeded"
  ],

  "x-paneResolution": {
    "explicit": {
      "price":      "Main price overlay",
      "oscillator": "Standard sub-pane below price",
      "<custom>":   "Any other non-empty string routes to a named sub-pane; equal strings group together"
    },
    "fallback": {
      "study.overlay=true":  "pane = price",
      "study.overlay=false": "pane = oscillator"
    },
    "authoringRule": "Oscillator-scale series (RSI, MACD, Stoch, BB %, etc.) MUST set pane = oscillator or a named sub-pane."
  },

  "definitions": {
    "TreeScriptScalarType": { "enum": ["int","float","bool","color","string"] },
    "TreeScriptSeriesType": { "enum": ["series_float","series_bool"] },
    "TreeScriptType":       { "anyOf": [{ "$ref": "#/definitions/TreeScriptScalarType" }, { "$ref": "#/definitions/TreeScriptSeriesType" }] },
    "BuiltinSeries": {
      "enum": ["open","high","low","close","volume","hl2","hlc3","ohlc4","bar_index","time"]
    },

    "TreeScriptInputDecl": {
      "type": "object",
      "required": ["id","type","default"],
      "properties": {
        "id":      { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
        "type":    { "enum": ["int","float","bool","color","string","series_float"] },
        "default": { "type": ["number","string","boolean"] },
        "constraints": {
          "type": "object",
          "properties": {
            "min":  { "type": "number" },
            "max":  { "type": "number" },
            "enum": { "type": "array", "items": { "type": "string" } }
          },
          "additionalProperties": false
        }
      }
    },

    "NodeBase": {
      "type": "object",
      "required": ["id","kind","type"],
      "properties": {
        "id":   { "type": "string" },
        "kind": { "enum": ["input_ref","series_ref","literal","history","binary","logical","ternary","stdlib_call","record_get"] },
        "type": { "$ref": "#/definitions/TreeScriptType" }
      }
    },

    "BinaryOp":  { "enum": ["add","sub","mul","div","mod","gt","lt","gte","lte","eq","neq"] },
    "LogicalOp": { "enum": ["and","or","not"] },

    "TreeScriptNode": {
      "oneOf": [
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "input_ref"  }, "input_id": { "type": "string" } }, "required": ["input_id"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "series_ref" }, "series":   { "$ref": "#/definitions/BuiltinSeries" } }, "required": ["series"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "literal"    }, "value":    { "type": ["number","string","boolean"] } }, "required": ["value"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "history"    }, "source":   { "type": "string" }, "lookback": { "type": "integer", "minimum": 1, "maximum": 5000 } }, "required": ["source","lookback"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "binary"     }, "op": { "$ref": "#/definitions/BinaryOp" }, "left": { "type": "string" }, "right": { "type": "string" } }, "required": ["op","left","right"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "logical"    }, "op": { "$ref": "#/definitions/LogicalOp" }, "left": { "type": "string" }, "right": { "type": "string" } }, "required": ["op","left"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "ternary"    }, "cond": { "type": "string" }, "then_branch": { "type": "string" }, "else_branch": { "type": "string" } }, "required": ["cond","then_branch","else_branch"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "stdlib_call"}, "fn": { "type": "string", "pattern": "^(ta|math)\\.[a-z_]+$" }, "args": { "type": "array", "items": { "type": "string" } } }, "required": ["fn","args"] }] },
        { "allOf": [{ "$ref": "#/definitions/NodeBase" }, { "properties": { "kind": { "const": "record_get" }, "source": { "type": "string" }, "field": { "type": "string" } }, "required": ["source","field"] }] }
      ]
    },

    "TreeScriptEmit": {
      "oneOf": [
        {
          "type": "object",
          "required": ["kind","source","title"],
          "properties": {
            "kind":   { "const": "plot" },
            "source": { "type": "string" },
            "title":  { "type": "string", "minLength": 1 },
            "color":  { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
            "pane":   { "type": "string", "minLength": 1 }
          },
          "additionalProperties": false
        },
        {
          "type": "object",
          "required": ["kind","source","title","severity"],
          "properties": {
            "kind":     { "const": "signal" },
            "source":   { "type": "string" },
            "title":    { "type": "string", "minLength": 1 },
            "severity": { "enum": ["info","warning","critical"] }
          },
          "additionalProperties": false
        },
        {
          "type": "object",
          "required": ["kind","source","title"],
          "properties": {
            "kind":    { "const": "alert" },
            "source":  { "type": "string" },
            "title":   { "type": "string", "minLength": 1 },
            "message": { "type": "string" }
          },
          "additionalProperties": false
        }
      ]
    },

    "TreeScriptIR": {
      "type": "object",
      "required": ["irVersion","studyName","overlay","inputs","nodes","emits","sourceHash","compiledAt","compilerVersion"],
      "properties": {
        "irVersion":       { "const": "1.0.0" },
        "studyName":       { "type": "string", "minLength": 1 },
        "overlay":         { "type": "boolean" },
        "inputs":          { "type": "array", "maxItems": 64,   "items": { "$ref": "#/definitions/TreeScriptInputDecl" } },
        "nodes":           { "type": "array", "maxItems": 2000, "items": { "$ref": "#/definitions/TreeScriptNode" } },
        "emits":           { "type": "array", "maxItems": 64,   "items": { "$ref": "#/definitions/TreeScriptEmit" } },
        "sourceHash":      { "type": "string", "pattern": "^[0-9a-f]{64}$" },
        "compiledAt":      { "type": "string", "format": "date-time" },
        "compilerVersion": { "const": "1.0.0" }
      },
      "additionalProperties": false
    }
  },

  "type": "object",
  "$ref": "#/definitions/TreeScriptIR"
}
