3 puntos por GN⁺ 2025-05-31 | Aún no hay comentarios. | Compartir por WhatsApp
  • C3 está basado en el lenguaje C y ofrece funciones avanzadas como módulos, sobrecarga de operadores, genéricos y ejecución en tiempo de compilación
  • Mantiene la sintaxis familiar de C, pero incorpora sintaxis que refuerza la productividad y la estabilidad como manejo de errores, defer y foreach
  • La introducción de contratos declarativos, tipos opcionales y mecanismos de manejo de errores mejora la seguridad y la claridad
  • Se ofrece un entorno de desarrollo práctico con biblioteca estándar e integración del sistema de build, además de asignación de memoria temporal
  • Tiene similitudes con el lenguaje Zig en aspectos como build, creación de proyectos y estructura del código, y deja ver nuevos experimentos de diseño de lenguaje

Resumen y características de C3

¿Qué es C3?

  • C3 es un lenguaje construido sobre el C existente, que mantiene una sintaxis familiar y al mismo tiempo ofrece funciones difíciles de lograr en C, como sistema de módulos, sobrecarga de operadores, genéricos, ejecución en tiempo de compilación, manejo de errores, defer, value methods, contratos graduales, slices, foreach y soporte de tipos dinámicos
  • Usa una estructura de módulos con namespaces para evitar colisiones de nombres (abc::Context, con namespaces imperativos)
  • Su objetivo principal es aumentar la productividad y ofrecer funciones modernas de programación de sistemas de forma segura

Características del lenguaje

Ejemplo Hello World

  • Sintácticamente es similar a C
  • En la declaración de funciones se debe usar explícitamente la palabra clave fn
  • Las funciones de la biblioteca estándar para entrada/salida son potentes, y permiten imprimir directamente varios tipos

Bucle foreach

  • A diferencia de C, incluye soporte nativo para sintaxis foreach
  • En los bucles por referencia se escribe & antes del nombre de la variable (función avanzada)
  • Soporta break y continue, y es similar al foreach de otros lenguajes

Bucle while

  • Antes de C99 no se podían usar declaraciones dentro de la condición de while, pero en C3 sí es posible

enum y sentencia switch

  • La sentencia switch admite break implícito (la mezcla de break implícito y explícito puede generar opiniones divididas)
  • Con la palabra clave nextcase se admite un salto explícito entre casos (facilita implementar tablas de salto)
  • Permite controlar de forma concisa flujos switch-case que en lenguajes como Zig o C eran más complejos

Palabra clave defer

  • Al salir del scope, las sentencias programadas con defer se ejecutan en orden inverso, garantizando de forma segura la limpieza de recursos
  • Puede usarse defer junto con catch y try para controlar el flujo de manejo de errores

struct y union

  • Dentro de un struct se permiten sub-structs/unions con nombre o anónimos, lo que facilita diseñar patrones de tagged union
  • Se distingue de forma estricta entre formas anónimas (duplicación de campos con el mismo nombre) y colisiones de nombres

Manejo de errores

  • El símbolo ? brinda soporte para tipos opcionales, unificando errores y valores opcionales para mayor comodidad
  • La palabra clave catch permite bifurcar cuando hay estado vacío (sin Optional) o error
  • A diferencia de Rust y Zig, la distinción entre error y valor opcional es más débil (ventaja: simplicidad, desventaja: menor claridad conceptual)
  • El operador ! (rethrow) permite propagar excepciones

Contratos (Contracts)

  • Las precondiciones y postcondiciones de una función (Require/Ensure) se escriben entre <* .. *> (las condiciones se verifican al compilar)
  • También admite análisis de fold en tiempo de compilación (el análisis estático aún no está implementado)

Métodos de struct

  • Los métodos asociados se definen con especificación de tipo + notación con punto (Foo.next), con namespace incluido (también para primitivos)
  • Se permiten métodos en todo tipo de tipos, como structs, unions y enums

Macros

  • Macros basadas en evaluación en tiempo de compilación (palabra clave macro)
  • Usa $ para parámetros de tiempo de compilación y # para pasar valores antes de evaluar
  • Estilo C (minimiza problemas de macros enredadas, enfatiza estabilidad del AST, verificación con prefijo @, etc.)
  • La reflexión de tipos y la ejecución en tiempo de compilación se gestionan mediante macros

Propiedades de tipos

  • alignof, kindof, extnameof, sizeof, typeid, methodsof, has_tagof, tagof, is_eq, is_ordered, is_substruct, etc.
  • Son adecuadas para metaprogramación y reflexión

Literales Base64/Hex

  • Permite declarar directamente secuencias de bytes con formas como b64"..." y x"..."
  • La necesidad puede reemplazarse con la macro integrada $embed (en la práctica se usa poco)

Tipos primitivos

  • Incluye varios tipos básicos como int, uint, char (siempre unsigned), bool, float, int128/uint128, etc.
  • Tiene tipos separados para punteros/tamaños como iptr, uptr, isz y usz (algo menos intuitivos)
  • A diferencia de C, garantiza el tamaño en bits

Otros

  • Incluye un conjunto amplio de funciones como sobrecarga de operadores, subtipado de structs, genéricos, runtime dispatch, tipo any y bitstructs

Práctica: experiencia usando C3

Instalación de C3

  • Soporta tanto binarios precompilados del sitio oficial como compilación directa desde el código fuente
  • Requiere instalar LLVM y LLD (si hay problemas de enlace, usar los flags de CMake -DLLVM_DIR y -DLLD_DIR)
  • Debido a que algunas distribuciones no incluyen la biblioteca LLD, se recomienda descargar directamente el binario
  • El compilador de C3 requiere dependencia de libtinfo

Creación de proyecto

  • El comando c3c init genera la estructura estándar de carpetas (LICENSE/README.md/project.json/src, etc.)
  • Configura la base del proyecto con Build, targets de compilación y ajustes de fuentes (similar a Zig o Cargo)
  • El archivo main.c3 por defecto es muy conciso (opinión: adecuado para nuevos usuarios)

Crear una calculadora

Diseño y objetivo

  • Se practica la implementación de un parser descendente recursivo (Recursive Descent Parser) y la lógica central de una calculadora, usando en C3 varias construcciones como funciones, entrada/salida, gestión de memoria y bucles
  • El objetivo es identificar directamente qué tan intuitiva es la sintaxis y cuáles son sus puntos fuertes o incómodos en productividad real

Procesamiento de entrada

  • Usa @pool para un asignador temporal (tmem), con liberación automática de memoria al salir del scope (arena allocator)
  • Soporta tmem (temporal) y mem (general) como esquema estándar de gestión de memoria, además del patrón de pasar allocators por función (mezcla ventajas de Zig y C)
  • La función main debe declarar siempre su valor de retorno (el compilador lo exige)
  • Las funciones cuyo valor de retorno puede ignorarse sin problema se marcan con el atributo @maydiscard (evita ignorarlas maliciosamente)

Implementación del tokenizer

  • Descompone la entrada del usuario en una lista de tokens
  • Aprovecha varias construcciones de control del lenguaje, como List, sintaxis foreach y switch-case (nextcase, combinación de break implícito/explicito) de la biblioteca estándar de C3
  • Hay cierta confusión con la sintaxis de slices (ambos índices extremos incluidos) y con los slices de longitud 0 (existe una sintaxis separada para especificar longitud)
  • La combinación de asignadores temporales/generales muestra transparencia y flexibilidad en la gestión de memoria, superior a la de lenguajes como Rust

Implementación del parser

  • Experiencia de codificación directa del parser (omitida)

Conclusión y opinión general

  • C3 busca el punto de encuentro entre los lenguajes de sistemas tradicionales y el diseño moderno
  • Fue diseñado estudiando Zig, Rust y C, como un lenguaje que busca compatibilizar rendimiento y estabilidad del código
  • Destacan funciones como modularidad, manejo seguro de memoria/errores/contratos, metaprogramación potente y sistema de build intuitivo
  • La curva de aprendizaje permite una entrada gradual para quienes ya tienen experiencia con C
  • Aún necesita mejorar en puntos como el ecosistema inmaduro de language server e IDE, y ciertas decisiones sintácticas que pueden gustar o no
  • Vale la pena seguirlo como un posible lenguaje alternativo de próxima generación para desarrollo low-level y de sistemas

Aún no hay comentarios.

Aún no hay comentarios.