Code Walkthrough¶
For testing, you should extend code from this section.
Common Utils¶
import os
import sys
import re
import sys
from jinja2 import Environment, BaseLoader
Template = Environment(loader=BaseLoader, block_start_string="<?", block_end_string="?>",variable_start_string="<?=", variable_end_string="?>")
def entity_to_struct(blob, archetypes):
a_types = blob["archetypes"]
atypes_of_struct = []
for a in a_types:
for b in archetypes:
if b["name"] == a:
atypes_of_struct.append(b)
final_c_fields = blob["c_fields"]
for e in atypes_of_struct:
final_c_fields += e["c_fields"]
return "struct {\n" + final_c_fields + "\n} " + blob["name"] + ";\n\n"
def write_file(name, fn, stash):
_ = stash
code = Template.from_string(_[name]).render(_=_)
f = open(fn, "w")
f.write(code)
f.close()
def cpp(file):
filename, file_extension = os.path.splitext(file)
filename, ignore = os.path.splitext(filename) # ignore gen
os.system("clang -E " + file + " > " + (filename + ".cpp" + file_extension))
def pre_process(file, stash):
_ = stash
filename, file_extension = os.path.splitext(file)
filename, ignore = os.path.splitext(filename) # ignore cpp
os.system("python tq.py < " + file + " > " + filename + ".tq" + file_extension)
os.system("python ns.py < " + filename + ".tq" + file_extension + " > " + filename + file_extension)
def compile(program, file):
os.system("scons -s")
def output_printer(text):
print(text, end="")
def word_split(word):
return [char for char in word]
def output_capture():
final_str = ""
def getter():
nonlocal final_str
return final_str
def setter(x):
nonlocal final_str
final_str += x
return [getter, setter]
Archetype¶
For the time being yaml is used intead of full fledged syntax. Archetype is compile time version of js prototypes. Not related to ECS archetype. Come up with a better name ?
archetypes:
- name: test
c_fields: |
int x;
Entity¶
Todo
Automatically generate ctor / dtor / getter / setter
entity:
name: test
archetypes: ["test"]
c_fields: |
int y;
Parsers¶
Ideally each parser can parse the complete C spec + New Syntax. Logic for piping between multiple syntaxes.
Source -> Syntax Parser 1 -> Generated C code 1
Generated C Code 1 -> Syntax Parser 2 -> Generated C Code 2
…
Todo
Use a proper lexer from pycparser
Namespaces¶
Very difficult to implement properly as you will have to manage the symbol table and typedefs. Currently “:” is translated to “_”.
import os
import sys
import re
from flexicon import Lexer
from source.utils import *
def parse_ns(text, printer):
EXPRESSION_LEXER = Lexer().simple(
(r'([A-Za-z0-9_:]+)\(', lambda x: ('NS',x)),
(r'(.|\s)', lambda x: ('C', x)),
)
tokens = EXPRESSION_LEXER.lex(text)
count = 1
line_count = 1
for e in tokens:
if e[0] == "NS":
name = e[1]
printer(name.replace(":","_") + "(")
if e[0] == "C":
printer(e[1])
test = """
foo:baz()
END
"""
if __name__ == "__main__":
parse_ns(sys.stdin.read(), output_printer)
Heredocs¶
import os
import sys
import re
from flexicon import Lexer
from source.utils import *
def parse_hd(text, printer):
EXPRESSION_LEXER = Lexer().simple(
(r'<<<([A-Z]+)\n', lambda x: ('TQ',x)),
(r'([^\s]+?)', lambda x: ('C', x)),
(r'(\s)', lambda x: ('SP', x)),
)
tokens = EXPRESSION_LEXER.lex(text)
count = 1
line_count = 1
in_hd = False
hd = ""
end = ""
for e in tokens:
if e[0] == "TQ":
in_hd = True
end = e[1]
if e[0] == "C":
if in_hd:
hd += e[1]
if hd.endswith(end):
size = len(hd)
mod_string = hd[:size - (len(end))]
in_hd = False
end = ""
for l in mod_string.split("\n"):
printer("\"" + l + "\\n\"")
printer("\n")
printer(";")
else:
printer(e[1])
if e[0] == "SP":
if in_hd:
hd += e[1]
else:
printer(e[1])
test = """
char[] x = <<<END
foo
bar
END
"""
if __name__ == "__main__":
parse_hd(sys.stdin.read(), output_printer)
Generics¶
<? macro sort(type) -?>
void swap(<?= type ?>* a, <?= type ?>* b)
{
<?= type ?> t = *a;
*a = *b;
*b = t;
}
int partition (<?= type ?> arr[], int low, int high)
{
int pivot = arr[high]; // pivot
int i = (low - 1); // Index of smaller element
for (int j = low; j <= high- 1; j++)
{
if (arr[j] <= pivot)
{
i++; // increment index of smaller element
swap(&arr[i], &arr[j]);
}
}
swap(&arr[i + 1], &arr[high]);
return (i + 1);
}
void quickSort(<?= type ?> arr[], int low, int high)
{
if (low < high)
{
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
<?- endmacro ?>
Headers¶
Currently headers are causing errors in pycparser as non standard C extensions are used in them. So including them separately for now.
#include <libdill.h>
#include <stdio.h>
#include <gc.h>
#define malloc(x) GC_malloc(x)
#define calloc(n,x) GC_malloc((n)*(x))
#define realloc(p,x) GC_realloc((p),(x))
#define free(x) (x) = NULL
#include <fio.h>
#include <fio_cli.h>
#include <http.h>
Scons¶
env = Environment(platform='posix')
Program('example.c', LIBS=[
'libdill',
'libgc',
'libfacil.io'
], CPPPATH = [
'vendor/libdill-master',
'vendor/bdwgc-master',
'vendor/appname/libdump/all'
], LIBPATH=[
'vendor/libdill-master',
'vendor/bdwgc-master',
'vendor/appname'
])
Main¶
<?= _["Headers"] ?>
<?= sort("int") ?>
<?= _["Entity"] ?>
void print_array(int arr[], int size) {
int i;
for (i=0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}
coroutine void worker(int count, const char *text) {
int i;
for(i = 0; i != count; ++i) {
printf("%s\n", text);
msleep(now() + 10);
}
}
void initialize_cli(int argc, char const *argv[]);
static void on_http_request(http_s *h) {
http_send_body(h, "Hello World!", 12);
}
int main(int argc, char **argv) {
GC_set_all_interior_pointers(0);
GC_INIT();
char greeting[] = <<<END
Hello World
END
printf("Hello World");
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr) / sizeof(arr[0]);
quickSort(arr, 0, n-1);
printf("Sorted array: \n");
print:array(arr, n);
go(worker(4, "a"));
msleep(now() + 100);
for (int i = 0; i < 20000; ++i) {
malloc(4096);
}
printf("Final heap size is %lu\n", (unsigned long)GC_get_heap_size());
initialize_cli(argc, argv);
/* listen for inncoming connections */
if (http_listen(fio_cli_get("-p"), fio_cli_get("-b"),
.on_request = on_http_request,
.max_body_size = fio_cli_get_i("-maxbd"),
.public_folder = fio_cli_get("-public"),
.log = fio_cli_get_bool("-log"),
.timeout = fio_cli_get_i("-keep-alive")) == -1) {
/* listen failed ?*/
perror(
"ERROR: facil.io couldn't initialize HTTP service (already running?)");
exit(1);
}
fio_start(.threads = fio_cli_get_i("-t"), .workers = fio_cli_get_i("-w"));
return 0;
}
void initialize_cli(int argc, char const *argv[]) {
fio_cli_start(
argc, argv, 0, 0, NULL,
// Address Binding arguments
FIO_CLI_PRINT_HEADER("Address Binding:"),
FIO_CLI_INT("-port -p port number to listen to. defaults port 3000"),
"-bind -b address to listen to. defaults any available.",
// Concurrency arguments
FIO_CLI_PRINT_HEADER("Concurrency:"),
FIO_CLI_INT("-workers -w number of processes to use."),
FIO_CLI_INT("-threads -t number of threads per process."),
// HTTP Settings arguments
FIO_CLI_PRINT_HEADER("HTTP Settings:"),
"-public -www public folder, for static file service.",
FIO_CLI_INT(
"-keep-alive -k HTTP keep-alive timeout (0..255). default: 10s"),
FIO_CLI_INT(
"-max-body -maxbd HTTP upload limit in Mega Bytes. default: 50Mb"),
FIO_CLI_BOOL("-log -v request verbosity (logging)."),
// WebSocket Settings arguments
FIO_CLI_PRINT_HEADER("WebSocket Settings:"),
FIO_CLI_INT("-ping websocket ping interval (0..255). default: 40s"),
FIO_CLI_INT("-max-msg -maxms incoming websocket message "
"size limit in Kb. default: 250Kb"),
"-redis -r an optional Redis URL server address.",
FIO_CLI_PRINT("\t\ta valid Redis URL would follow the pattern:"),
FIO_CLI_PRINT("\t\t\tredis://user:password@localhost:6379/"));
/* Test and set any default options */
if (!fio_cli_get("-p")) {
/* Test environment as well */
char *tmp = getenv("PORT");
if (!tmp)
tmp = "3000";
/* Set default (unlike cmd line arguments, aliases are manually set) */
fio_cli_set("-p", tmp);
fio_cli_set("-port", tmp);
}
if (!fio_cli_get("-b")) {
char *tmp = getenv("ADDRESS");
if (tmp) {
fio_cli_set("-b", tmp);
fio_cli_set("-bind", tmp);
}
}
if (!fio_cli_get("-public")) {
char *tmp = getenv("HTTP_PUBLIC_FOLDER");
if (tmp) {
fio_cli_set("-public", tmp);
fio_cli_set("-www", tmp);
}
}
if (!fio_cli_get("-public")) {
char *tmp = getenv("REDIS_URL");
if (tmp) {
fio_cli_set("-redis-url", tmp);
fio_cli_set("-ru", tmp);
}
}
}