Skip to content

Commit

Permalink
Merge pull request #13 from chibash/module
Browse files Browse the repository at this point in the history
support import declarations
  • Loading branch information
maejima-fumika authored Oct 3, 2024
2 parents 8aad9c6 + f957925 commit ef15981
Show file tree
Hide file tree
Showing 15 changed files with 694 additions and 121 deletions.
9 changes: 8 additions & 1 deletion server/src/transpiler/classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,25 @@ export class InstanceType extends ObjectType {
private numOfMethods: number
private superClass: ObjectType
private className: string
private blueScriptName: string

constructor(name: string, superClass: ObjectType) {
constructor(name: string, bsName: string, superClass: ObjectType) {
super()
this.superClass = superClass
this.className = name
this.blueScriptName = bsName

this.numOfProperties = superClass instanceof InstanceType ? superClass.objectSize() : 0
this.numOfMethods = superClass instanceof InstanceType ? superClass.methodCount() : 0
}

// returns a globally-unique class name. It may be different from the
// original name given in the source code.
name() { return this.className }

// the name given in the source code.
override sourceName() { return this.blueScriptName }

superType(): ObjectType | null { return this.superClass }

superclass(): ObjectType { return this.superClass }
Expand Down
30 changes: 15 additions & 15 deletions server/src/transpiler/code-generator/c-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import * as AST from '@babel/types'
import { ErrorLog } from '../utils'
import { Integer, Float, Boolean, String, Void, Null, Any,
import { Integer, Float, BooleanT, StringT, Void, Null, Any,
ObjectType, objectType, FunctionType,
StaticType, isPrimitiveType, typeToString, ArrayType, sameType, encodeType, isSubtype, } from '../types'
import { InstanceType, ClassTable } from '../classes'
Expand Down Expand Up @@ -49,11 +49,11 @@ function typeToCType2(type: StaticType): string {
return 'int32_t'
case Float:
return 'float'
case Boolean:
case BooleanT:
return 'int32_t'
case Void:
return 'void'
case String:
case StringT:
case Null:
case Any:
return anyTypeInC
Expand Down Expand Up @@ -97,20 +97,20 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un
case Integer:
if (from === Float)
return '(int32_t)('
else if (from === Boolean)
else if (from === BooleanT)
return '('
else if (from === Any)
return 'safe_value_to_int('
else
break
case Float:
if (from === Integer || from === Boolean)
if (from === Integer || from === BooleanT)
return '(float)('
else if (from === Any)
return 'safe_value_to_float('
else
break
case Boolean:
case BooleanT:
if (from === Integer || from === Float)
return '('
else if (from === Any)
Expand All @@ -128,14 +128,14 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un
return 'int_to_value('
case Float:
return 'float_to_value('
case Boolean:
case BooleanT:
return 'bool_to_value('
default:
return '('
}
default: // "to" is either String, Object, or Array
if (from === Any || from instanceof ObjectType) {
if (to === String)
if (to === StringT)
return 'safe_value_to_string('
else if (to instanceof ObjectType) {
if (to === objectType)
Expand All @@ -145,7 +145,7 @@ export function typeConversion(from: StaticType | undefined, to: StaticType | un
return 'safe_value_to_intarray('
else if (to.elementType === Float)
return 'safe_value_to_floatarray('
else if (to.elementType === Boolean)
else if (to.elementType === BooleanT)
return 'safe_value_to_bytearray('
else if (to.elementType === Any)
return 'safe_value_to_anyarray('
Expand Down Expand Up @@ -311,7 +311,7 @@ export function arrayElementGetter(t: StaticType | undefined, arrayType: StaticT
return '(*gc_intarray_get('
else if (t === Float)
return '(*gc_floatarray_get('
else if (t === Boolean)
else if (t === BooleanT)
return '(*gc_bytearray_get('
else
return `(*gc_array_get(`
Expand All @@ -334,7 +334,7 @@ export function arrayFromElements(t: StaticType) {
return 'gc_make_intarray('
else if (t === Float)
return 'gc_make_floatarray('
else if (t === Boolean)
else if (t === BooleanT)
return 'gc_make_bytearray('
else if (t === Any)
return 'gc_make_array(1, '
Expand All @@ -347,7 +347,7 @@ export function arrayFromSize(t: StaticType) {
return 'gc_new_intarray('
else if (t === Float)
return 'gc_new_floatarray('
else if (t === Boolean)
else if (t === BooleanT)
return 'gc_new_bytearray('
else if (t === Any)
return 'gc_new_array(1, '
Expand All @@ -360,14 +360,14 @@ export function actualElementType(t: StaticType) {
return Integer
else if (t === Float)
return Float
else if (t === Boolean)
return Boolean
else if (t === BooleanT)
return BooleanT
else
return Any
}

export function getArrayLengthIndex(t: StaticType) {
if (t === Boolean)
if (t === BooleanT)
return 1
else
return 0
Expand Down
79 changes: 59 additions & 20 deletions server/src/transpiler/code-generator/code-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import * as AST from '@babel/types'
import { runBabelParser, ErrorLog, CodeWriter } from '../utils'
import { Integer, Float, Boolean, String, Void, Null, Any,
ObjectType, FunctionType,
StaticType, isPrimitiveType, encodeType, sameType, typeToString, ArrayType, objectType, isSubtype } from '../types'
import { Integer, BooleanT, Void, Any, ObjectType, FunctionType,
StaticType, isPrimitiveType, encodeType, sameType, typeToString, ArrayType, objectType } from '../types'
import * as visitor from '../visitor'
import { getCoercionFlag, getStaticType } from '../names'
import { typecheck } from '../type-checker'
Expand All @@ -13,16 +12,29 @@ import { VariableInfo, VariableEnv, GlobalEnv, FunctionEnv, VariableNameTableMak
import * as cr from './c-runtime'
import { InstanceType } from '../classes'

// codeId: an integer more than zero. It is used for generating a unique name.
/*
Transpile BlueScript code.
codeId: an integer more than zero. It is used for generating a unique name.
src: the source code for transpilation.
gvnt: a name table for global variables.
importer: a handler for processing an import declaration. It takes a module name and returns
a name table. It may throw an error message of string type or an ErrorLog object.
moduleId: module identifier. It must be >= 0 if this code is a module. Otherwise, it must be -1.
It is used to generate a unique identifier.
startLine: the line number for the first line of the given source code.
header: the code inserted in the generated code by transpilation. It should be #include directives.
*/
export function transpile(codeId: number, src: string, gvnt?: GlobalVariableNameTable,
importer?: (name: string) => GlobalVariableNameTable, moduleId: number = -1,
startLine: number = 1, header: string = '') {
const ast = runBabelParser(src, startLine);
const maker = new VariableNameTableMaker()
const maker = new VariableNameTableMaker(moduleId)
const nameTable = new GlobalVariableNameTable(gvnt)
typecheck(ast, maker, nameTable)
typecheck(ast, maker, nameTable, importer)
const nullEnv = new GlobalEnv(new GlobalVariableNameTable(), cr.globalRootSetName)
const mainFuncName = `${cr.mainFunctionName}${codeId}`
const generator = new CodeGenerator(mainFuncName, codeId)
const mainFuncName = `${cr.mainFunctionName}${codeId}_${moduleId < 0 ? '' : moduleId}`
const generator = new CodeGenerator(mainFuncName, codeId, moduleId)
generator.visit(ast, nullEnv) // nullEnv will not be used.
if (generator.errorLog.hasError())
throw generator.errorLog
Expand All @@ -42,13 +54,15 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
private externalMethods: Map<string, StaticType>
private uniqueId = 0
private uniqueIdCounter = 0
private moduleId = -1

constructor(initializerName: string, codeId: number) {
constructor(initializerName: string, codeId: number, moduleId: number) {
super()
this.initializerName = initializerName
this.globalRootSetName = `${cr.globalRootSetName}${codeId}`
this.globalRootSetName = moduleId < 0 ? `${cr.globalRootSetName}${codeId}` : `${cr.globalRootSetName}${codeId}_${moduleId}`
this.externalMethods = new Map()
this.uniqueId = codeId
this.moduleId = moduleId
}

getCode(header: string) {
Expand All @@ -70,7 +84,7 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
this.signatures += cr.externClassDef(objectType)
this.signatures += '\n'
const externalRoots: { [key: string]: number } = {}
env2.forEachExternalVariable((name, type, index) => {
env2.forEachExternalVariable((info, name, type, index) => {
if (name === undefined) {
// a type name only
if (type instanceof ObjectType) {
Expand All @@ -90,7 +104,7 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
externalRoots[name] = index
}
else if (type instanceof FunctionType)
this.signatures += this.makeFunctionStruct(name, type)
this.signatures += this.makeFunctionStruct(name, type, info.isConst)
else
this.signatures += `extern ${cr.typeToCType(type, name)};\n`
})
Expand Down Expand Up @@ -155,7 +169,10 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
throw this.errorLog.push('fatal: unknown identifier', node)
}

private makeUniqueName() { return `fn_${this.uniqueId}_${this.uniqueIdCounter++}`}
private makeUniqueName() {
const mid = this.moduleId < 0 ? '' : this.moduleId
return `fn_${mid}_${this.uniqueId}_${this.uniqueIdCounter++}`
}

private makeFunctionObject(name: string, fenv?: FunctionEnv) {
let obj = 'VALUE_UNDEF'
Expand Down Expand Up @@ -578,7 +595,7 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
if (decl.init && AST.isArrowFunctionExpression(decl.init)) {
const func = decl.init
const funcName = (decl.id as AST.Identifier).name
this.functionBodyDeclaration(func, funcName, env)
this.functionBodyDeclaration(func, funcName, env, false)
return true
}
}
Expand All @@ -597,9 +614,11 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
extern struct func_body _foo;
static int32_t fbody_foo(value_t self, int32_t _n) { ... function body ... }
struct func_body _foo = { fbody_foo, "(i)i" };
fbody_foo() will be a non-static function if isStatic is false.
*/
functionBodyDeclaration(node: AST.FunctionDeclaration | AST.ArrowFunctionExpression,
funcName: string, env: VariableEnv): FunctionEnv {
funcName: string, env: VariableEnv, isStatic: boolean = true): FunctionEnv {
const fenv = new FunctionEnv(getVariableNameTable(node), env)
fenv.allocateRootSet()
const funcType = getStaticType(node) as FunctionType;
Expand All @@ -609,14 +628,18 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {

const prevResult = this.result
this.result = this.declarations
this.functionBody(node, fenv, funcType, bodyName)
if (isStatic)
this.functionBody(node, fenv, funcType, bodyName)
else
this.functionBody(node, fenv, funcType, bodyName, '') // not a static function

this.result = prevResult

const fname = transpiledFuncName
if (fenv.isFreeVariable(funcInfo))
this.result.nl().write(`${fname}.${cr.functionPtr} = ${bodyName};`).nl()
else {
this.signatures += this.makeFunctionStruct(fname, funcType)
this.signatures += this.makeFunctionStruct(fname, funcType, false)
this.declarations.write(`${cr.funcStructInC} ${fname} = { ${bodyName}, "${encodeType(funcType)}" };`).nl()
}

Expand Down Expand Up @@ -719,8 +742,24 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {
return sig + ')'
}

private makeFunctionStruct(name: string, type: StaticType) {
return `extern ${cr.funcStructInC} ${name};\n`
private makeSimpleParameterList(funcType: FunctionType) {
let sig = `(${cr.anyTypeInC} self`
for (let i = 0; i < funcType.paramTypes.length; i++) {
sig += `, ${cr.typeToCType(funcType.paramTypes[i], `p${i}`)}`
}

return sig + ')'
}

private makeFunctionStruct(name: string, type: FunctionType, isConst: boolean) {
let body: string = ''
if (isConst) {
const bodyName = cr.functionBodyName(name)
const sig = this.makeSimpleParameterList(type)
body = `extern ${cr.typeToCType(type.returnType, bodyName)}${sig};\n`
}

return `extern ${cr.funcStructInC} ${name};\n${body}`
}

arrowFunctionExpression(node: AST.ArrowFunctionExpression, env: VariableEnv): void {
Expand Down Expand Up @@ -846,7 +885,7 @@ export class CodeGenerator extends visitor.NodeVisitor<VariableEnv> {

const left_type = this.needsCoercion(left)
const right_type = this.needsCoercion(right)
if ((left_type === Boolean || right_type === Boolean)
if ((left_type === BooleanT || right_type === BooleanT)
// if either left or right operand is boolean, the other is boolean
|| (left_type === Any || right_type === Any)) {
this.result.write(`${cr.typeConversion(left_type, Any, left)}`)
Expand Down
Loading

0 comments on commit ef15981

Please sign in to comment.