Permalink
Browse files

Pluggable frontends, first shot

  • Loading branch information...
1 parent c6f2975 commit bd8df2b9a929745816b1c4fdd3990a98bbc9440e Amos Wenger committed May 17, 2011
View
11 make
@@ -13,17 +13,6 @@ fi
echo Library directory is $LIBDIR
-if [[ ! -e $NAGAQUEEN_DIST ]]; then
- export NAGAQUEEN_DIST=../nagaqueen
-fi
-
-if [[ ! -e .libs/NagaQueen.o ]]; then
- echo "Compiling nagaqueen"
- greg $NAGAQUEEN_DIST/grammar/nagaqueen.leg > .libs/NagaQueen.c || exit
- gcc -fPIC -w -c -std=c99 -D__OOC_USE_GC__ .libs/NagaQueen.c -O3 -o .libs/NagaQueen.o $C_FLAGS || exit 1
- rock -v -libfolder=$NAGAQUEEN_DIST/source .libs/NagaQueen.o -dynamiclib=$LIBDIR/libnagaqueen.so || exit 1
-fi
-
export OOC_LIBS=..
OOC_FLAGS="-v -g -nolines +-rdynamic"
View
84 source/core/DynamicLoader.ooc
@@ -4,6 +4,8 @@ import io/File
import backend/Backend
import frontend/BuildParams
+import frontend/[ParsingPool, Frontend]
+
DynamicLoader: class {
plugins: static File
@@ -34,32 +36,69 @@ DynamicLoader: class {
exit(0)
}
- loadBackend: static func (name: String, params: BuildParams) -> Backend {
- prefix := name + "_backend"
-
- path := ""
+ findPlugin: func (name: String, callback: Func (LibHandle)) {
plugins getChildren() each(|child|
if(child name() startsWith?(prefix)) {
- if(params verbose > 0) "Found backend %s in %s" printfln(child path, name)
- path = child path
+ if(params verbose > 0) "Found plug-in %s in %s" printfln(name, child path)
+
+ handle := dlopen(child path, RTLD_LAZY)
+ if(handle) {
+ callback(handle)
+ } else {
+ "Error while opening plug-in %s: %s" printfln(path, dlerror())
+ }
} else {
if(params verbose > 0) "Ignoring %s" printfln(child path)
}
)
+ }
+
+ loadBackend: static func (name: String, params: BuildParams) -> Backend {
+ backend: Backend = null
+ findPlugin(name + "_backend", |handle|
+ classPrefix := "backend_%s_Backend_" format(name)
- if(!path empty?()) {
- handle := dlopen(path, RTLD_LAZY)
+ // call load
+ loadAddress := dlsym(handle, classPrefix + "load")
+ if(!loadAddress) {
+ "Symbol '%s' not found in %s" printfln(classPrefix + "load", name)
+ dlclose(handle)
+ return null
+ }
+
+ callableLoad: Closure
+ callableLoad thunk = loadAddress
+ (callableLoad as Func)()
- if(!handle) {
- "Error while opening pluggable backend %s: %s" printfln(path, dlerror())
+ // call constructor
+ constructorAddress := dlsym(handle, classPrefix + "Backend_new")
+ if(!constructorAddress) {
+ "Symbol '%s' not found in %s" printfln(constructorSymbolName, name)
+ dlclose(handle)
return null
}
+ callableConstructor: Closure
+ callableConstructor thunk = constructorAddress
+
+ backend = (callableConstructor as Func -> Backend)()
+ if(!backend) {
+ "Couldn't instantiate backend for '%s', please report this bug to backend maintainers" printfln(name)
+ }
+ if(params verbose > 0) "Got backend %s" printfln(backend class name)
+ )
+ backend
+ }
+
+ loadFrontend: static func (name: String, pool: ParsingPool) -> FrontendFactory {
+ factory: FrontendFactory = null
+ findPlugin(name + "_frontend", |handle|
+ classPrefix := "frontend_%s_FrontendFactory_" format(name)
+
// call load
- loadSymbolName := "backend_%s_Backend_load" format(name)
- loadAddress := dlsym(handle, loadSymbolName)
+ loadAddress := dlsym(handle, classPrefix + "load")
if(!loadAddress) {
- "Symbol '%s' not found in backend %s" printfln(loadSymbolName, path)
+ "Symbol '%s' not found in %s" printfln(classPrefix + "load", name)
dlclose(handle)
return null
}
@@ -69,26 +108,23 @@ DynamicLoader: class {
(callableLoad as Func)()
// call constructor
- constructorSymbolName := "backend_%s_Backend__%s_Backend_new" format(name, name)
- constructorAddress := dlsym(handle, constructorSymbolName)
+ constructorAddress := dlsym(handle, classPrefix + "FrontendFactory_new")
if(!constructorAddress) {
- "Symbol '%s' not found in backend %s" printfln(constructorSymbolName, path)
+ "Symbol '%s' not found in %s" printfln(constructorSymbolName, name)
dlclose(handle)
return null
}
callableConstructor: Closure
callableConstructor thunk = constructorAddress
- backend: Backend
- backend = (callableConstructor as Func -> Backend)()
- if(!backend) {
- "Couldn't instantiate backend for '%s', please report this bug to backend maintainers" printfln(name)
+ factory = (callableConstructor as Func -> FrontendFactory)()
+ if(!factory) {
+ "Couldn't instantiate frontend for '%s', please report this bug to frontend maintainers" printfln(name)
}
- if(params verbose > 0) "Got backend %s" printfln(backend class name)
- return backend
- }
- null
+ if(params verbose > 0) "Got frontend %s" printfln(factory class name)
+ )
+ factory
}
}
View
21 source/core/backend/Backend.ooc
@@ -2,10 +2,31 @@ import ast/[Module, Access, Var]
import middle/Resolver
import frontend/BuildParams
+/**
+ * Interface for pluggable backends in oc-c89
+ */
Backend: abstract class {
+ /**
+ * Given the full AST of a module and a set of build parameters, process
+ * should compile the AST into the desired result.
+ *
+ * A C backend might produce C files, then call a C compiler to produce
+ * a library/executable, for instance.
+ *
+ * A bytecode backend might produce backend and save it in files.
+ *
+ * An interpreter backend might simply interpret the module.
+ */
process: abstract func (module: Module, params: BuildParams)
+ /**
+ * Override this method to have a backend-specific way to resolve variable
+ * accesses.
+ *
+ * For example, a C backend might automatically be aware of C types from
+ * parsing header files, and allow resolving of those names automatically.
+ */
resolveAccess: func (acc: Access, task: Task, suggest: Func (Var)) {
"resolveAccess(%s) in %s" printfln(acc toString(), class name)
}
View
285 source/core/frontend/AstBuilder.ooc
@@ -1,285 +0,0 @@
-use nagaqueen
-
-import structs/[Stack, List, HashMap], io/File
-
-import nagaqueen/OocListener
-
-import ParsingPool
-
-import ast/[Module, FuncDecl, Call, Statement, Type, Expression,
- Var, Access, StringLit, NumberLit, Import, Node, Return]
-import middle/Resolver
-
-/**
- * Used to parse multi-vars declarations, ie.
- *
- * a, b, c: Int
- */
-VarStack: class {
-
- type: Type
- vars := Stack<Var> new()
-
-}
-
-AstBuilder: class extends OocListener {
-
- module: Module
- stack := Stack<Object> new()
-
- pool: ParsingPool
-
- init: func (=pool) {}
-
- parse: func (path: String) {
- try {
- module = Module new(path substring(0, -5))
- stack push(module)
- super(path)
- } catch (e: Exception) {
- e print()
- }
- }
-
- /*
- * Stack handling functions
- */
- pop: func <T> (T: Class) -> T {
- v := stack pop()
- if(!v instanceOf?(T)) Exception new("Expected " + T name + ", pop'd " + v class name) throw()
- v
- }
-
- peek: func <T> (T: Class) -> T {
- v := stack peek()
- if(!v instanceOf?(T)) Exception new("Expected " + T name + ", peek'd " + v class name) throw()
- v
- }
-
- stackString: func -> String {
- b := Buffer new()
- stack each(|el|
- b append(match el {
- case n: Node => n toString()
- case => el class name
- }). append("->")
- )
- b toString()
- }
-
- /*
- * Import
- */
- onImport: func (path, name: CString) {
- nullPath := (path == null || (path as Char*)[0] == '\0')
- importName := nullPath ? path toString() + name toString() : name toString()
- _import := Import new(importName)
- peek(Module) imports add(_import)
-
- // FIXME: this is a very very dumb strategy to get the real path of an Import
- // but oh well, I'm testing ParsingPool right now.
- realPath := File new(File new(module fullName) parent() path, importName) path + ".ooc"
-
- // FIXME: and what about caching? huh?
- pool push(ParsingJob new(realPath, _import))
- }
-
- /*
- *
- */
- onInclude: func (name: CString) {
- peek(Module) includes add(name toString())
- }
-
-
- /*
- * Functions
- */
- onFunctionStart: func (name, doc: CString) {
- fd := FuncDecl new()
- fd name = name toString()
- stack push(fd)
- }
-
- onFunctionEnd: func -> FuncDecl {
- fd := pop(FuncDecl)
- if(stack peek() instanceOf?(Module)) {
- var := Var new(fd name)
- fd name = ""
- var expr = fd
- onStatement(var)
- }
- fd
- }
-
- onFunctionArgsStart: func {
- stack push(peek(FuncDecl) args)
-
- }
-
- onFunctionArgsEnd: func {
- stack pop() // args
- }
-
-
- onFunctionBody: func {
- // ignore
- }
-
- onFunctionAttr: func (f: FuncAttributes, value: CString = null) {
- fd := peek(FuncDecl)
- match f {
- case FuncAttributes _extern =>
- fd externName = value toString()
- case =>
- "Unknown function attribute %d" printfln(f)
- }
- }
-
- onFunctionReturnType: func (returnType: Type) {
- peek(FuncDecl) retType = returnType
- }
-
- /*
- * Function calls
- */
-
- onFunctionCallStart: func (name: CString) {
- stack push(Call new(Access new(null, name toString())))
- }
-
- onFunctionCallArg: func (arg: Expression) {
- peek(Call) args add(arg)
- }
-
- onFunctionCallExpr: func (call: Call, expr: Expression) {
- call subject = Access new(expr, call subject name)
- }
-
- onFunctionCallEnd: func -> Call {
- pop(Call)
- }
-
- /* Variable declarations */
-
- onVarDeclStart: func {
- stack push(VarStack new())
- }
-
- onVarDeclEnd: func -> Object {
- pop(VarStack)
- }
-
-
- onVarDeclName: func (name, doc: CString) {
- vStack := peek(VarStack)
- vStack vars push(Var new(name toString()))
- }
-
- onVarDeclExpr: func (expr: Expression) {
- peek(VarStack) vars peek() expr = expr
- }
-
- onVarDeclType: func (type: Type) {
- peek(VarStack) vars each(|v|
- v _type = type
- )
- }
-
- /* Types */
-
- onTypeNew: func (name: CString) -> Type {
- BaseType new(name toString())
- }
-
- // FuncType
-
- onFuncTypeNew: func -> Object {
- FuncType new(FuncDecl new())
- }
-
- onFuncTypeGenericArgument: func (type: FuncType, name: CString) {
- "Got generic argument <%s> for funcType %s" printfln(name, type toString())
- }
-
- onFuncTypeArgument: func (funcType: FuncType, argType: Type) {
- "Got typeArgument %s" printfln(argType toString())
- funcType proto args put("", v := Var new(""). setType(argType))
- }
-
- onFuncTypeVarArg: func (funcType: Object) {
- UnsupportedAstElement new(class, "func-type-vararg") throw()
- }
-
- onFuncTypeReturnType: func (funcType: FuncType, returnType: Type) {
- "Got returnType %s" printfln(returnType toString())
- funcType proto retType = returnType
- }
-
- /* Various expression/statements */
-
- onStringLiteral: func (text: CString) -> StringLit {
- StringLit new(text toString())
- }
-
- onIntLiteral: func (format: IntFormat, value: CString) -> NumberLit {
- NumberLit new(format as NumberFormat, value toString())
- }
-
- onReturn: func (expr: Expression) -> Statement {
- Return new(expr)
- }
-
- onVarAccess: func (expr: Expression, name: CString) -> Access {
- Access new(expr, name toString())
- }
-
- /*
- * Statement
- */
- onStatement: func (statement: Statement) {
- match statement {
- case vStack: VarStack =>
- vStack vars each(|v|
- ("Popping var " + v toString())
- onStatement(v)
- )
- return
- }
-
- node := stack peek()
- match node {
- case fd: FuncDecl =>
- //"Got statement %s in function %s" printfln(statement toString(), fd toString())
- fd body add(statement)
- case mod: Module =>
- //"Got statement %s in module body" printfln(statement toString())
- mod body add(statement)
- match statement {
- case fd: FuncDecl =>
- fd global = true
- case vd: Var =>
- vd global = true
- }
- case =>
- match (node class) {
- case List =>
- list := node as List<Object>
- list add(statement)
- case HashMap =>
- hm := node as HashMap<String, Object>
- match statement {
- case v: Var =>
- hm put(v name, v)
- case =>
- ("Don't know how to react to statement " + statement toString() +
- " with a map on top of the stack.") println()
- }
- case =>
- ("Don't know how to react to statement " + statement toString() +
- " with " + node class name + " on top of the stack.") println()
- }
- }
- }
-
-}
View
33 source/core/frontend/Frontend.ooc
@@ -0,0 +1,33 @@
+
+import ParsingPool
+import ast/Module
+
+/**
+ * Interface for pluggable frontends
+ */
+Frontend: abstract class {
+
+ module: Module
+ pool: ParsingPool
+
+ /**
+ * Create a new frontend, able to parse .ooc files, attached to a given
+ * ParsingPool. The pool is used to trigger the parsing of imported .ooc
+ * files
+ */
+ init: func (=pool) {}
+
+ /**
+ * Given the path to an .ooc file, the frontend should parse it and produce
+ * an AST for the module. It can trigger the parsing of imported .ooc files
+ * using pool push()
+ */
+ parse: abstract func (path: String)
+
+}
+
+FrontendFactory: abstract class {
+
+ init: func (pool: ParsingPool) {}
+
+}
View
7,936 source/core/frontend/NagaQueen.c
0 additions, 7,936 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
10 source/core/frontend/ParsingPool.ooc
@@ -1,5 +1,5 @@
-import AstBuilder
+import Frontend
import ast/[Module, Import]
import middle/Resolver
@@ -21,14 +21,18 @@ ParsingPool: class {
todo := ArrayList<ParsingJob> new()
done := ArrayList<ParsingJob> new()
workers := ArrayList<ParserWorker> new()
+
+ factory: FrontendFactory
active := true
doneMutex, todoMutex: Mutex
init: func {
doneMutex = Mutex new()
- todoMutex = Mutex new()
+ todoMutex = Mutex new()
+ // TODO: make frontends choosable from the command line
+ factory = DynamicLoader loadFrontend("nagaqueen", this)
}
push: func (j: ParsingJob) {
@@ -98,7 +102,7 @@ ParserWorker: class {
if(job) {
busy = true
"Parsing %s [%d]" printfln(job path, id)
- builder := AstBuilder new(pool)
+ builder := pool factory create()
builder parse(job path)
job module = builder module
if(job _import) job _import module = builder module

0 comments on commit bd8df2b

Please sign in to comment.