<commit_before>package tink.sql.macros;

import haxe.macro.Context;
import tink.macro.BuildCache;
import haxe.macro.Expr;

using tink.MacroApi;

class TableBuilder {

  static function build() {
    return BuildCache.getType('tink.sql.Table', function (ctx:BuildContext) {
      return
        switch ctx.type {
          case TAnonymous(_.get() => { fields: [{ kind: FVar(_, _), name: name, type: _.reduce() => TAnonymous(_.get().fields => fields) }] } ):
            
            var cName = ctx.name;
            var names = [for (f in fields) f.name];
            
            var rowTypeFields = new Array<Field>(),
                fieldsTypeFields = new Array<Field>(),
                fieldsExprFields = [],
                fieldsValues = [];
                
            var rowType = TAnonymous(rowTypeFields),
                fieldsType = TAnonymous(fieldsTypeFields);//caution: these are mutable until the function is done          
                
            for (f in fields) {
              var fType = f.type.reduce().toComplex(),
                  fName = f.name;
              //var fStruct = TAnonymous([{
                //name: fName,
                //kind: FVar(fType),
                //pos: f.pos,
              //}]);
              
              rowTypeFields.push({ 
                pos: f.pos,
                name: fName,
                kind: FProp('default', 'null', fType),
                meta: {
                  var m = [];
                  if(f.meta.extract(':optional').length > 0) m.push({name: ':optional', pos: f.pos});
                  m;
                },
              });
              
              var followedType = f.type.reduce().toComplex();
              fieldsTypeFields.push({
                pos: f.pos,
                name: fName,
                kind: FProp('default', 'null', macro : tink.sql.Expr.Field<$fType, $rowType>)
                //kind: FProp('default', 'null', macro : tink.sql.Expr<$fType>)
              });
              
              fieldsExprFields.push({
                field: f.name,
                expr: macro new tink.sql.Expr.Field($v{name}, $v{f.name}),
              });
              
              fieldsValues.push({
                var name = macro $v{fName};
                var nullable = f.meta.has(':optional');
                
                function resolveType(type:haxe.macro.Type) {
                  return switch type {
                    case TType(_.get() => {pack: [], name: 'Null'}, [p]):
                      nullable = true;
                      resolveType(p);
                    case TType(_.get() => {module: 'tink.sql.types.Integer'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DInt($v{maxLength}, false, $v{f.meta.has(':autoIncrement')});
                    
                    case TType(_.get() => {module: 'tink.sql.types.Number'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DFloat($v{maxLength});
                    
                    case TType(_.get() => {module: 'tink.sql.types.Text'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DString($v{maxLength});
                    
                    case TType(_.get() => {module: 'tink.sql.types.Blob'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DBlob($v{maxLength});
                    
                    case TType(_.get() => {module: 'tink.sql.types.DateTime'}, _):
                      macro tink.sql.Info.DataType.DDateTime;
                      
                    case _.getID() => 'Date': // should merge with the prev case: https://github.com/HaxeFoundation/haxe/issues/6327
                      macro tink.sql.Info.DataType.DDateTime;
                    
                    case TType(_.get() => {module: 'tink.sql.types.Point'}, p):
                      macro tink.sql.Info.DataType.DPoint;
                    
                    case _.getID() => 'Bool':
                      macro tink.sql.Info.DataType.DBool;
                    
                    case _.getID() => 'tink.sql.types.Id':
                      var maxLength = 12; // TODO: make these configurable
                      macro tink.sql.Info.DataType.DInt($v{maxLength}, false, $v{f.meta.has(':autoIncrement')});
                    
                    case TAbstract(_.get() => {type: type}, _):
                      resolveType(type);
                      
                    case _.getID() => v:
                      if(v == null) v = Std.string(type);
                      f.pos.error('Unsupported type $v. Use types from the tink.sql.types package.');
                  }
                }
                
                var type = resolveType(f.type);
                var primary = f.meta.has(':primary');
                var unique = f.meta.has(':unique'); // primary already implies unique
                
                var key =
                  if(primary) macro haxe.ds.Option.Some(tink.sql.Info.KeyType.Primary);
                  else if(unique) macro haxe.ds.Option.Some(tink.sql.Info.KeyType.Unique);
                  else macro haxe.ds.Option.None;
                
                EObjectDecl([
                  {field: 'name', expr: name},
                  {field: 'nullable', expr: macro $v{nullable}},
                  {field: 'type', expr: type},
                  {field: 'key', expr: key},
                ]).at(f.pos);
              });
            }
            <commit_msg>Break infinite loop when using core types as underlying type of an abstract.<commit_after>package tink.sql.macros;

import haxe.macro.Context;
import tink.macro.BuildCache;
import haxe.macro.Expr;

using tink.MacroApi;

class TableBuilder {

  static function build() {
    return BuildCache.getType('tink.sql.Table', function (ctx:BuildContext) {
      return
        switch ctx.type {
          case TAnonymous(_.get() => { fields: [{ kind: FVar(_, _), name: name, type: _.reduce() => TAnonymous(_.get().fields => fields) }] } ):
            
            var cName = ctx.name;
            var names = [for (f in fields) f.name];
            
            var rowTypeFields = new Array<Field>(),
                fieldsTypeFields = new Array<Field>(),
                fieldsExprFields = [],
                fieldsValues = [];
                
            var rowType = TAnonymous(rowTypeFields),
                fieldsType = TAnonymous(fieldsTypeFields);//caution: these are mutable until the function is done          
                
            for (f in fields) {
              var fType = f.type.reduce().toComplex(),
                  fName = f.name;
              //var fStruct = TAnonymous([{
                //name: fName,
                //kind: FVar(fType),
                //pos: f.pos,
              //}]);
              
              rowTypeFields.push({ 
                pos: f.pos,
                name: fName,
                kind: FProp('default', 'null', fType),
                meta: {
                  var m = [];
                  if(f.meta.extract(':optional').length > 0) m.push({name: ':optional', pos: f.pos});
                  m;
                },
              });
              
              var followedType = f.type.reduce().toComplex();
              fieldsTypeFields.push({
                pos: f.pos,
                name: fName,
                kind: FProp('default', 'null', macro : tink.sql.Expr.Field<$fType, $rowType>)
                //kind: FProp('default', 'null', macro : tink.sql.Expr<$fType>)
              });
              
              fieldsExprFields.push({
                field: f.name,
                expr: macro new tink.sql.Expr.Field($v{name}, $v{f.name}),
              });
              
              fieldsValues.push({
                var name = macro $v{fName};
                var nullable = f.meta.has(':optional');
                
                function resolveType(type:haxe.macro.Type) {
                  return switch type {
                    case TType(_.get() => {pack: [], name: 'Null'}, [p]):
                      nullable = true;
                      resolveType(p);
                    case TType(_.get() => {module: 'tink.sql.types.Integer'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DInt($v{maxLength}, false, $v{f.meta.has(':autoIncrement')});
                    
                    case TType(_.get() => {module: 'tink.sql.types.Number'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DFloat($v{maxLength});
                    
                    case TType(_.get() => {module: 'tink.sql.types.Text'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DString($v{maxLength});
                    
                    case TType(_.get() => {module: 'tink.sql.types.Blob'}, p):
                      var maxLength = getInt(p[0], f.pos);
                      macro tink.sql.Info.DataType.DBlob($v{maxLength});
                    
                    case TType(_.get() => {module: 'tink.sql.types.DateTime'}, _):
                      macro tink.sql.Info.DataType.DDateTime;
                      
                    case _.getID() => 'Date': // should merge with the prev case: https://github.com/HaxeFoundation/haxe/issues/6327
                      macro tink.sql.Info.DataType.DDateTime;
                    
                    case TType(_.get() => {module: 'tink.sql.types.Point'}, p):
                      macro tink.sql.Info.DataType.DPoint;
                    
                    case _.getID() => 'Bool':
                      macro tink.sql.Info.DataType.DBool;
                    
                    case _.getID() => 'tink.sql.types.Id':
                      var maxLength = 12; // TODO: make these configurable
                      macro tink.sql.Info.DataType.DInt($v{maxLength}, false, $v{f.meta.has(':autoIncrement')});
                    
                    case TAbstract(_.get() => {name: name, type: type}, _):
                      switch type {
                        case TAbstract(_.get() => {name: core, meta: meta}, _) if(meta.has(':coreType')):
                          f.pos.error('$core as underlying type for the abstract $name is unsupported. Use types from the tink.sql.types package.');
                        default:
                          resolveType(type);
                      }
                      
                    case _.getID() => v:
                      if(v == null) v = Std.string(type);
                      f.pos.error('Unsupported type $v. Use types from the tink.sql.types package.');
                  }
                }
                
                var type = resolveType(f.type);
                var primary = f.meta.has(':primary');
                var unique = f.meta.has(':unique'); // primary already implies unique
                
                var key =
                  if(primary) macro haxe.ds.Option.Some(tink.sql.Info.KeyType.Primary);
                  else if(unique) macro haxe.ds.Option.Some(tink.sql.Info.KeyType.Unique);
                  else macro haxe.ds.Option.None;
                
                EObjectDecl([
                  {field: 'name', expr: name},
                  {field: 'nullable', expr: macro $v{nullable}},
                  {field: 'type', expr: type},
                  {field: 'key', expr: key},
                ]).at(f.pos);
              });
            }
            
{'fraction_non_alphanumeric': 0.13121019108280255, 'fraction_numerical': 0.0018683651804670912, 'mean_word_length': 1.1831664812754912, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 44, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1063389', 'n_tokens_mistral': 3258, 'n_tokens_neox': 3070, 'n_words': 652}