diff --git a/hexa.json b/hexa.json
index a953631..8eb0d4a 100644
--- a/hexa.json
+++ b/hexa.json
@@ -26,6 +26,10 @@
"data/project",
"data/compilerError",
"data/hints",
+ "toHexa/clang/clangTree",
+ "toHexa/clang/clangMinifier",
+ "toHexa/clang/clangGenerator",
+ "toHexa/clang/clangMain",
"targets",
"cli/args",
"cli/help",
diff --git a/source/cli.hexa b/source/cli.hexa
index 06b9d27..ca4198b 100644
--- a/source/cli.hexa
+++ b/source/cli.hexa
@@ -1,5 +1,5 @@
// The Hexa Compiler
-// Copyright (C) 2021-2023 Oleh Petrenko
+// Copyright (C) 2021-2024 Oleh Petrenko
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
@@ -27,6 +27,10 @@
case "version": printVersion()
case "symlink": symlink()
case "init": init()
+ case "c2hexa":
+ // TODO add to help
+ process.argv.shift()
+ c2hexa(process.argv)
// TODO `null` | "repl" opens REPL
case _:
try {
diff --git a/source/toHexa/clang/clangGenerator.hexa b/source/toHexa/clang/clangGenerator.hexa
new file mode 100644
index 0000000..590d86e
--- /dev/null
+++ b/source/toHexa/clang/clangGenerator.hexa
@@ -0,0 +1,226 @@
+// The Hexa Compiler
+// Copyright (C) 2024 Oleh Petrenko
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see .
+
+// TODO some C++
+// TODO rename reserved keywords like `fun`
+// TODO header-only mode to produce .hexa bindings
+/// Actual code generator used by toHexa/Clang
+class ClangGenerator {
+ new (project ClangProject) {
+ for file in project.files {
+ if not file.generate {
+ continue
+ }
+
+ let node ClangNode = JSON.parse(Fs.readFileSync(file.input).toString())
+ console.log('Found ' + node.inner.length + ' root syntax nodes.')
+
+ let result [Node] = node.inner.filter(root => root.kind != null).map(root => nodeToNode(root)).filter(node => node != null)
+
+ // TODO Would be much better just be allowed to `node.stringify()` static call where `fun stringify(node /*infer*/ = this)` thus allowing non-vTable @struct methods too!
+ let string = result.map(node => stringify(node)).join('\n\n')
+ // TODO run autoformat over the result
+ // TODO comment header
+ Fs.writeFileSync(file.output, string)
+ }
+ }
+
+ fun toCamelCase(text: String): String {
+ if text.length == 1 {
+ return text.toLowerCase()
+ }
+
+ return text.substr(0, 1).toLowerCase() + text.substr(1)
+ }
+
+ fun toPascalCase(text: String): String {
+ if text.length == 1 {
+ return text.toUpperCase()
+ }
+
+ return text.substr(0, 1).toUpperCase() + text.substr(1)
+ }
+
+ fun typeOfReturn(of ClangType) ClangType {
+ return {
+ qualType: of.qualType.split(' (')[0]
+ }
+ }
+
+ fun typeToType(of ClangType) NodeType {
+ return NodeType.Type(toPascalCase(of.qualType), null)
+ }
+
+ fun nodeToNode(node ClangNode) Node? {
+ switch node.kind {
+ // Roots
+ case VarDecl: {
+ let name = toCamelCase(node.name)
+ let varType = typeToType(node.type)
+ var initializer Node? = null
+
+ if let inner = node.inner, let value = inner[0] {
+ initializer = nodeToNode(value)
+ }
+
+ return Node.Var(name, varType, initializer, false, false)
+ }
+ case RecordDecl:
+ case TypedefDecl:
+ case FunctionDecl: {
+ let name = toCamelCase(node.name)
+ let args [Node] = []
+ var returnType NodeType = [] // TODO why [] is okay here
+ var returnType NodeType = typeToType(typeOfReturn(node.type))
+ var body Node? = null
+ // TODO FORMAT
+ let nodes = node.inner ??([] as! [ClangNode] /*TODO*/)
+
+ for functionDecl in nodes {
+ switch functionDecl.kind {
+ case ParmVarDecl:
+ args.push(Node.Var(toCamelCase(functionDecl.name), typeToType(functionDecl.type), null /*TODO*/, false, false))
+ }
+ }
+
+ for functionDecl in nodes {
+ switch functionDecl.kind {
+ case CompoundStmt:
+ body = nodeToNode(functionDecl)
+ // TODO CompoundStmt happens once?
+ case ParmVarDecl: // Ok
+ case _: // TODO
+ }
+ }
+
+ return Node.Function(name, body, args, returnType, false /*TODO*/, false /*TODO*/)
+ }
+
+ case IfStmt:
+ case DeclStmt:
+ case FieldDecl:
+ case BinaryOperator:
+ case CompoundAssignOperator:
+ case IntegerLiteral:
+ case WhileStmt:
+ case DoStmt:
+ case ForStmt:
+ case SEHTryStmt:
+ case SwitchStmt:
+ case CaseStmt:
+ case DefaultStmt:
+ case BreakStmt:
+ case GCCAsmStmt:
+ case NullStmt:
+ case ContinueStmt:
+ case UnaryOperator:
+ case ReturnStmt:
+ case CallExpr:
+ case ArraySubscriptExpr:
+ case CompoundStmt:
+ case UnaryExprOrTypeTraitExpr:
+ case CharacterLiteral:
+ case OffsetOfExpr:
+ case AtomicExpr:
+ case VAArgExpr:
+ case InitListExpr:
+ case CompoundLiteralExpr:
+ case ImplicitCastExpr:
+ case StringLiteral:
+ case ConstantExpr:
+ case CStyleCastExpr:
+ case DeclRefExpr:
+ case ParenExpr:
+ case ConditionalOperator:
+ case FloatingLiteral:
+ case MemberExpr:
+ case ParmVarDecl:
+ case TextComment:
+ case ParagraphComment:
+ case BlockCommandComment:
+ case ParamCommandComment:
+ case FullComment:
+ case BuiltinType:
+ case SEHExceptStmt:
+ case EnumDecl:
+ case EmptyDecl: return null // ;
+ case GotoStmt:
+ case LabelStmt:
+ case PredefinedExpr:
+
+ case null: return null // Never happens
+ }
+
+ return Node.String("// Unknown Clang node kind: " + node.kind)
+ }
+
+ static fun stringify(node Node?) String {
+ switch node {
+ case String(s): return "'\(s)'"
+ case Ident(name): return name
+ case Bool(b): return b? "true" : "false"
+ case Int(s): return s.toString()
+ case Float(s): return s.toString()
+ case Null: return "null"
+ case This: return "this"
+ case Parenthesis(expr): return "(" + stringify(expr) + ")"
+ case Index(expr, index): return stringify(expr) + '[' + stringify(index) + ']'
+ case Dot(expr, name): return stringify(expr) + '.' + name
+ case DotUpper(expr, name): return stringify(expr) + '.' + name
+ case Call(e, args, argNames):
+ let arg = []
+ for i in args.length {
+ if let name = argNames[i] {
+ arg.push(name + ': ' + stringify(args[i]))
+ } else {
+ arg.push(stringify(args[i]))
+ }
+ }
+ return stringify(e) + '(' + arg.join(', ') + ')'
+ case Array(elements): return '[' + [for el in elements stringify(el)].join(', ') + ']'
+ case Binop(a, op, b): return stringify(a) + ' ' + Token.stringify(op) + ' ' + stringify(b)
+ case Object(names, el):
+ return '{' + [for i in el.length names[i] + ': ' + stringify(el[i])].join(', ') + '}'
+ case NodeTypeValue(t):
+ return DataHelper.extractTypeName(t)
+ case Static(f): return 'static ' + stringify(f)
+ case Var(name, t, expr, const, external): {
+ var body = ''
+ if let expr = expr {
+ body = ' = ' + stringify(expr)
+ }
+ return
+ (external? 'declare ' : '') +
+ (const? 'let ' : 'var ') + name + ' ' + NodeType.stringify(t) + body
+ }
+ case Function(name, body, vars, returnType, external, variadic):
+ if name == null {
+ // TODO `// Anonymous`
+ return `fun`
+ }
+
+ return 'fun ' + name + `(` + vars.map(v => switch v {
+ case Var(name, t, expr, const, external):
+ name + ' ' + NodeType.stringify(t)
+ }).join(', ') + `) ` + NodeType.stringify(returnType) + ' ' + stringify(body)
+ case null:
+ return ''
+ case _:
+ // TODO exhaustive
+ console.error('Cannot stringify', node)
+ return "..."
+ }
+ }
+}
diff --git a/source/toHexa/clang/clangMain.hexa b/source/toHexa/clang/clangMain.hexa
new file mode 100644
index 0000000..382d38f
--- /dev/null
+++ b/source/toHexa/clang/clangMain.hexa
@@ -0,0 +1,49 @@
+// The Hexa Compiler
+// Copyright (C) 2024 Oleh Petrenko
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see .
+
+/// Config file used by toHexa/Clang
+interface ClangProject {
+ let files: [{
+ input: String,
+ generate: Bool,
+ output: String
+ }]
+}
+
+/// Entry point of the toHexa/Clang
+fun c2hexa(arguments [String]) {
+ let action = arguments.shift()
+
+ switch action {
+ case 'minify':
+ // TODO only-header only-source
+ let input = arguments.shift()
+ let output = arguments.shift()
+ if let input = input, let output = output {
+ // TODO
+ } else {
+ console.error(quote('Command syntax is `hexa c2hexa minify input.json output.json`'))
+ }
+ case 'json':
+ let input = arguments.shift()
+ if let input = input {
+ let json ClangProject = JSON.parse(Fs.readFileSync(input).toString())
+ new ClangGenerator(json)
+ } else {
+ console.error(quote('Command syntax is `hexa c2hexa json project.json`'))
+ }
+ case _: console.error(quote('Unknown toHexa action `' + action + '`'))
+ }
+}
diff --git a/source/toHexa/clang/clangMinifier.hexa b/source/toHexa/clang/clangMinifier.hexa
new file mode 100644
index 0000000..ae4b40c
--- /dev/null
+++ b/source/toHexa/clang/clangMinifier.hexa
@@ -0,0 +1,14 @@
+// The Hexa Compiler
+// Copyright (C) 2024 Oleh Petrenko
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see .
diff --git a/source/toHexa/clang/clangTree.hexa b/source/toHexa/clang/clangTree.hexa
new file mode 100644
index 0000000..d98a3ae
--- /dev/null
+++ b/source/toHexa/clang/clangTree.hexa
@@ -0,0 +1,105 @@
+// The Hexa Compiler
+// Copyright (C) 2024 Oleh Petrenko
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program. If not, see .
+
+enum ClangKind : String {
+ IfStmt
+ DeclStmt
+ VarDecl
+ RecordDecl
+ FieldDecl
+ BinaryOperator
+ CompoundAssignOperator
+ IntegerLiteral
+ WhileStmt
+ DoStmt
+ ForStmt
+ SEHTryStmt
+ SwitchStmt
+ CaseStmt
+ DefaultStmt
+ BreakStmt
+ GCCAsmStmt
+ NullStmt
+ ContinueStmt
+ UnaryOperator
+ ReturnStmt
+ CallExpr
+ ArraySubscriptExpr
+ CompoundStmt
+ UnaryExprOrTypeTraitExpr
+ CharacterLiteral
+ OffsetOfExpr
+ AtomicExpr
+ VAArgExpr
+ InitListExpr
+ CompoundLiteralExpr
+ ImplicitCastExpr
+ StringLiteral
+ ConstantExpr
+ CStyleCastExpr
+ DeclRefExpr
+ ParenExpr
+ ConditionalOperator
+ FloatingLiteral
+ MemberExpr
+ ParmVarDecl
+ TextComment
+ ParagraphComment
+ BlockCommandComment
+ ParamCommandComment
+ FullComment
+ FunctionDecl
+ TypedefDecl
+ BuiltinType
+ SEHExceptStmt
+ EnumDecl
+ EmptyDecl
+ GotoStmt
+ LabelStmt
+ PredefinedExpr
+}
+
+interface ClangType {
+ let qualType: String
+ let typeAliasDeclId: String?
+ let desugaredQualType: String?
+}
+
+interface ClangNode {
+ let inner: [ClangNode]
+ let kind: ClangKind?
+ let type: ClangType?
+ let id: String
+ let name: String
+ let targetLabelDeclId: String
+ let declId: String
+ let mangledName: String
+ let text: String
+ let tagUsed: String
+ let param: String
+ let value: String // TODO | number
+ let opcode: String
+ let isImplicit: Bool
+ let hasElse: Bool
+ let referencedDecl: {
+ name: String
+ }
+ let loc: {
+ file: String
+ includedFrom: {
+ file: String
+ }
+ }
+}