if_lang 0.1.1

Intent-first functional IR language for LLM-friendly logic drafts
Documentation
extern fn list_len(list) explain { Returns list length. };
extern fn list_get(list, idx) explain { Returns list[idx]. };
extern fn list_set(list, idx, value) explain { Returns list with list[idx] replaced. };
extern fn list_push(list, value) explain { Returns list with value appended. };
extern fn str_split_ws(s) explain { Splits string by whitespace. };
extern fn str_trim_end_char(s, ch) explain { Trims one trailing char if present. };
extern fn str_to_int(s) explain { Parses string to Int. };
extern fn assert_eq(a, b) explain { Fails if a != b. };

// Constants
let tok_len_no_where = 4;
let tok_len_with_where = 8;
let tok_table = 3;
let tok_where_col = 5;
let tok_where_op = 6;
let tok_where_val = 7;
let index_not_found = -1;
let int_zero = 0;
let empty_expr = Int { value: 0 };

// AST / Data Model
data Table = Table { name, columns, rows };
data Db = Db { tables };
data Expr = Column { name } | Int { value } | Binary { op, left, right };
data Query = Query { table, has_where, where_expr };
data Result = Result { columns, rows };

// Catalog + Storage (row store)
fn db_open() = Db { tables: [] };

fn table_name(t) = match t {
  Table { name, columns, rows } => name;
  _ => "";
};

fn table_columns(t) = match t {
  Table { name, columns, rows } => columns;
  _ => [];
};

fn table_rows(t) = match t {
  Table { name, columns, rows } => rows;
  _ => [];
};

fn table_with_rows(t, new_rows) = match t {
  Table { name, columns, rows } => Table { name, columns, rows: new_rows };
  _ => t;
};

fn db_create_table(db, name, columns) = match db {
  Db { tables } => Db { tables: list_push(tables, Table { name, columns, rows: [] }) };
  _ => db;
};

fn db_insert(db, name, row) = match db {
  Db { tables } => Db { tables: insert_into_tables(0, tables, name, row) };
  _ => db;
};

fn insert_into_tables(idx, tables, name, row) =
  if idx >= list_len(tables) { tables }
  else {
    if table_name(list_get(tables, idx)) == name {
      list_set(
        tables,
        idx,
        table_with_rows(
          list_get(tables, idx),
          list_push(table_rows(list_get(tables, idx)), row)
        )
      )
    } else {
      insert_into_tables(idx + 1, tables, name, row)
    }
  };

// Query Executor (volcano style over rows)
fn db_query(db, sql) = match db {
  Db { tables } => query_tables(0, tables, parse_sql(sql));
  _ => Result { columns: [], rows: [] };
};

fn query_tables(idx, tables, query) =
  if idx >= list_len(tables) { Result { columns: [], rows: [] } }
  else {
    if table_name(list_get(tables, idx)) == query_table(query) {
      execute_query(query, list_get(tables, idx))
    } else {
      query_tables(idx + 1, tables, query)
    }
  };

fn execute_query(query, table) =
  if query_has_where(query) {
    Result {
      columns: table_columns(table),
      rows: filter_rows(0, table_rows(table), [], query_where_expr(query), table_columns(table))
    }
  } else {
    Result { columns: table_columns(table), rows: table_rows(table) }
  };

fn filter_rows(idx, rows, acc, expr, columns) =
  if idx >= list_len(rows) { acc }
  else {
    if eval_pred(expr, columns, list_get(rows, idx)) {
      filter_rows(idx + 1, rows, list_push(acc, list_get(rows, idx)), expr, columns)
    } else {
      filter_rows(idx + 1, rows, acc, expr, columns)
    }
  };

fn eval_pred(expr, columns, row) = match expr {
  Binary { op, left, right } =>
    compare_values(op, eval_expr_value(left, columns, row), eval_expr_value(right, columns, row));
  _ => false;
};

fn eval_expr_value(expr, columns, row) = match expr {
  Column { name } => column_value(columns, name, row);
  Int { value } => value;
  _ => int_zero;
};

fn column_value(columns, name, row) =
  if column_index(columns, name) < 0 { int_zero }
  else { list_get(row, column_index(columns, name)) };

fn column_index(columns, name) =
  find_col_index(0, columns, name);

fn find_col_index(idx, columns, name) =
  if idx >= list_len(columns) { index_not_found }
  else {
    if list_get(columns, idx) == name { idx }
    else { find_col_index(idx + 1, columns, name) }
  };

fn compare_values(op, left, right) =
  match op {
    ">" => left > right;
    ">=" => left >= right;
    "<" => left < right;
    "<=" => left <= right;
    "!=" => left != right;
    "==" => left == right;
    "=" => left == right;
    _ => false;
  };

// Parser (minimal SQL subset)
fn parse_sql(sql) =
  parse_sql_tokens(str_split_ws(str_trim_end_char(sql, ";")));

fn parse_sql_tokens(tokens) =
  if list_len(tokens) == tok_len_no_where {
    Query {
      table: list_get(tokens, tok_table),
      has_where: false,
      where_expr: empty_expr
    }
  } else {
    Query {
      table: list_get(tokens, tok_table),
      has_where: true,
      where_expr: parse_where_expr(tokens)
    }
  };

fn parse_where_expr(tokens) =
  Binary {
    op: list_get(tokens, tok_where_op),
    left: Column { name: list_get(tokens, tok_where_col) },
    right: Int { value: str_to_int(list_get(tokens, tok_where_val)) }
  };

fn query_table(q) = match q {
  Query { table, has_where, where_expr } => table;
  _ => "";
};

fn query_has_where(q) = match q {
  Query { table, has_where, where_expr } => has_where;
  _ => false;
};

fn query_where_expr(q) = match q {
  Query { table, has_where, where_expr } => where_expr;
  _ => empty_expr;
};

// Tests
let db0 = db_open();
let db1 = db_create_table(db0, "t1", ["c1", "c2"]);
let db2 = db_insert(db1, "t1", [1, "alpha"]);
let db3 = db_insert(db2, "t1", [2, "beta"]);
let db4 = db_insert(db3, "t1", [3, "gamma"]);
let db5 = db_insert(db4, "t1", [0, "zero"]);

let q1 = db_query(db5, "select * from t1 where c1 > 1;");
let expected1 = Result {
  columns: ["c1", "c2"],
  rows: [
    [2, "beta"],
    [3, "gamma"]
  ]
};
let ok1 = assert_eq(q1, expected1);

let q2 = db_query(db5, "select * from t1;");
let expected2 = Result {
  columns: ["c1", "c2"],
  rows: [
    [1, "alpha"],
    [2, "beta"],
    [3, "gamma"],
    [0, "zero"]
  ]
};
let ok2 = assert_eq(q2, expected2);

[ok1, ok2]