Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Comment code generator.
  • Loading branch information
coreh committed Apr 30, 2012
1 parent db73e60 commit 8bbaa0f
Showing 1 changed file with 93 additions and 5 deletions.
98 changes: 93 additions & 5 deletions lib/generator.js
@@ -1,13 +1,20 @@
var uuid = require('node-uuid')

var Type = require('./type')

/**
* Represents C code generated from a symbol in the syntax tree
*/
var Code = function() {
this.unit = ''
this.block = ''
this.inline = ''
}

/**
* Generates an intermediate representation of the program,
* in the form of standard C, to be passed to the backend.
*
* @param {Object} module The module provided by the compiler.
*/
var Generator = function(module) {
this.cTypes = { 'var' : { name: 'struct katana_var_t'
, unit: 'struct katana_var_t { long long int data[2]; };'
Expand All @@ -16,26 +23,47 @@ var Generator = function(module) {
this.module = module
}

/**
* These identifier names cannot be used for variables in the produced code.
*/
Generator.forbiddenIdentifiers = [
/* C keywords */
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do',
'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'int',
'long', 'register', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'
'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',

/* identifiers used by the compiler */
'closure'
]

Generator.prototype = {
/**
* Takes a katana identifier name and rewrites it so that it's a valid
* C identifier name
*
* @param {String} name
*/
rewriteName: function(name) {
if (name.match(/^\_\_/) || Generator.forbiddenIdentifiers.indexOf(name) != -1) {
return '__' + name
}
return name
},

/**
* Takes a katana type and works out the equivalent C type
*
* @param {Type} type
*/
toCType: function(type) {
switch(type.type) {
// Platform dependent (native) types
case 'int': return (process.arch == 'x64') ? 'long long int' : 'long int'
case 'uint': return (process.arch == 'x64') ? 'unsigned long long int' : 'unsigned long int'
case 'float': return (process.arch == 'x64') ? 'double' : 'float'

// Primitive Types
case 'int64': return 'long long int'
case 'int32': return 'long int'
case 'int16': return 'short int'
Expand All @@ -47,36 +75,52 @@ Generator.prototype = {
case 'float64': return 'double'
case 'float32': return 'float'
case 'void': return 'void'

// Pointers
case 'pointer': return this.toCType(type.subtypes[0]) + '*'

// Functions
case 'function':
// Check if we already have a c struct that represents this type
if (this.cTypes[type]) {
return this.cTypes[type].name
} else {
// Generate a struct that represents this type

// Mangle the function type to a valid C identifier, and use it as a struct name
var name = 'struct katana_' +
type.toString()
.replace(/\(/g, '_$$') // mangle the name to a valid C identifier
.replace(/\(/g, '_$$')
.replace(/\)/g, '$$_')
.replace(/\*/g, '_pointer')
.replace(/\,/g, '_') + 't'

// Recursively figure out the equivalent C types of the function arguments
var args = []
for (var i = 1; i < type.subtypes.length; i++) {
args.push(this.toCType(type.subtypes[i]))
}

// Handle functions with no arguments
if (args.length == 0) {
args.push('void')
}

// Generate a struct that represents the function type
var unit = name + '{ void * closure; ' + this.toCType(type.subtypes[0]) + ' (*function) (' + args.join(', ') + '); };';

// Store it and return
this.cTypes[type] = {
name: name
, unit: unit
}
return name
}

// Varying
case 'var':
return this.cTypes[type].name

// Literal types (default to largest possible value to avoid data loss)
default:
switch (type.family()) {
case 'uint': return 'unsigned long long int'
Expand All @@ -85,10 +129,20 @@ Generator.prototype = {
}
},

/**
* Generates an unique name for a specific role in the generated program
*
* @param {String} role
*/
uniqueName: function(role) {
return 'katana_' + role + '_' + uuid.v4().replace(/\-/g, '')
},

/**
* Generate code for a symbol, or for the whole program if no symbol is provided
*
* @param {Symbol} symbol (optional)
*/
generate: function(symbol) {
if (typeof symbol === 'undefined') {
var declarationCode = this.generateDeclarations(this.module.syntaxTree.meta.scope)
Expand All @@ -109,24 +163,47 @@ Generator.prototype = {
}
},

/**
* Generate C code for the declarations in the provided scope, rewriting all
* variable names into valid C identifiers in the process.
*
* If the current scope is a closure, this method is also responsible for
* generating all closure-related code.
*
* @param {Object} scope
*/
generateDeclarations: function(scope) {
var code = new Code()
if (scope.closure) {
// Generate code for a struct that will hold closure members
var closureName = 'struct ' + this.uniqueName('closure')
code.unit += closureName + ' {'

// Generate code for allocating the closure struct in the heap
code.block += closureName + ' *closure = calloc(1, sizeof(' + closureName + '));'

for (var name in scope.variables) {
// Rewrite variable name to valid C identifier
var rewrittenName = this.rewriteName(name)

// Add variable to closure struct
code.unit += this.toCType(scope.variables[name].type) + ' ' + rewrittenName + ';'

// If the variable is a function argument, copy its value to the closure struct
if (!scope.variables[name].free) {
code.block += 'closure->' + rewrittenName + ' = ' + name + ';'
}

// Rewrite variable name to acomodate for closure so that it can still be acessed normally
scope.variables[name].rewrittenName = 'closure->' + rewrittenName
}
code.unit += '};'
} else {
for (var name in scope.variables) {
// Rewrite variable name to valid C identifier
scope.variables[name].rewrittenName = this.rewriteName(name)

// Declare free (non-parameter) variables
if (scope.variables[name].free) {
code.block += this.toCType(scope.variables[name].type) + ' ' + scope.variables[name].rewrittenName + ';'
}
Expand All @@ -135,6 +212,12 @@ Generator.prototype = {
return code
},

/**
* Generate code for a typecast
*
* @param {Symbol} symbol
* @param {Type} type
*/
generateCast: function(symbol, type) {
if (symbol.meta.type.equals(type)) {
return this.generate(symbol)
Expand All @@ -149,6 +232,10 @@ Generator.prototype = {
return code
},

/**
* Symbols
*/

'statement list': function(statementList) {
var code = new Code()
statementList.children.forEach(function(child) {
Expand Down Expand Up @@ -215,6 +302,7 @@ Generator.prototype = {
declarationCode.block +
statementListCode.block +
'};'
// Temporary variable used to hold function literal
var tmpVarName = this.uniqueName('temp')
code.block += this.toCType(functionLiteral.meta.type) + ' ' + tmpVarName + ' = {' +
'(void *)closure, &'+name+
Expand Down

0 comments on commit 8bbaa0f

Please sign in to comment.