Skip to content

Commit

Permalink
[C2HEXA] Draft
Browse files Browse the repository at this point in the history
  • Loading branch information
PeyTy committed Jul 2, 2024
1 parent a081e6c commit 87848a4
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 1 deletion.
4 changes: 4 additions & 0 deletions hexa.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion source/cli.hexa
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 {
Expand Down
226 changes: 226 additions & 0 deletions source/toHexa/clang/clangGenerator.hexa
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

// 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 "..."
}
}
}
49 changes: 49 additions & 0 deletions source/toHexa/clang/clangMain.hexa
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.

/// 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 + '`'))
}
}
14 changes: 14 additions & 0 deletions source/toHexa/clang/clangMinifier.hexa
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
Loading

0 comments on commit 87848a4

Please sign in to comment.