ainedit: initial .jaf support

Initial support for compiling .jaf (System 4 language) files. So far
this is limited to adding struct definitions and declaring new globals.
The actual bytecode compilation is not yet implemented.

This Bison grammar is based on a C grammar and should cover 95% of the
System 4 language syntax already, though many AST nodes are not yet
implemented.
This commit is contained in:
Nunuhara Cabbage
2020-06-10 21:23:00 -07:00
parent 7b270e5325
commit 897cbde39c
11 changed files with 1781 additions and 7 deletions
+5
View File
@@ -168,6 +168,7 @@ struct ain_initval {
union {
char *string_value;
int32_t int_value;
float float_value;
};
};
@@ -321,6 +322,10 @@ void ain_decrypt(uint8_t *buf, size_t len);
struct ain_function *ain_get_function(struct ain *ain, char *name);
int ain_get_function_index(struct ain *ain, struct ain_function *f);
struct ain_struct *ain_get_struct(struct ain *ain, char *name);
int ain_get_struct_no(struct ain *ain, char *name);
int ain_add_struct(struct ain *ain, char *name);
struct ain_variable *ain_add_global(struct ain *ain, char *name);
void ain_add_initval(struct ain *ain, struct ain_initval *init);
void ain_free(struct ain *ain);
void ain_free_functions(struct ain *ain);
+45 -5
View File
@@ -61,15 +61,20 @@ static void init_func_ht(struct ain *ain)
}
}
static void struct_ht_add(struct ain *ain, struct ain_struct *s)
{
struct ht_slot *kv = ht_put(ain->_struct_ht, s->name);
if (kv->value) {
ERROR("Duplicate structure names: '%s'", s->name);
}
kv->value = s;
}
static void init_struct_ht(struct ain *ain)
{
ain->_struct_ht = ht_create(1024);
for (int i = 0; i < ain->nr_structures; i++) {
struct ht_slot *kv = ht_put(ain->_struct_ht, ain->structures[i].name);
if (kv->value) {
ERROR("Duplicate structure names: '%s'", ain->structures[i].name);
}
kv->value = &ain->structures[i];
struct_ht_add(ain, &ain->structures[i]);
}
}
@@ -183,6 +188,41 @@ struct ain_struct *ain_get_struct(struct ain *ain, char *name)
return ht_get(ain->_struct_ht, name);
}
int ain_get_struct_no(struct ain *ain, char *name)
{
struct ain_struct *s = ain_get_struct(ain, name);
return s ? s - ain->structures : -1;
}
int ain_add_struct(struct ain *ain, char *name)
{
ain->structures = xrealloc_array(ain->structures, ain->nr_structures, ain->nr_structures+1, sizeof(struct ain_struct));
ain->structures[ain->nr_structures].name = strdup(name);
ain->structures[ain->nr_structures].constructor = -1;
ain->structures[ain->nr_structures].destructor = -1;
struct_ht_add(ain, &ain->structures[ain->nr_structures]);
ain->nr_structures++;
return ain->nr_structures - 1;
}
struct ain_variable *ain_add_global(struct ain *ain, char *name)
{
int no = ain->nr_globals;
ain->globals = xrealloc_array(ain->globals, ain->nr_globals, ain->nr_globals+1, sizeof(struct ain_variable));
ain->globals[no].name = strdup(name);
if (ain->version >= 12)
ain->globals[no].name2 = strdup("");
ain->nr_globals++;
return &ain->globals[no];
}
void ain_add_initval(struct ain *ain, struct ain_initval *init)
{
ain->global_initvals = xrealloc_array(ain->global_initvals, ain->nr_initvals, ain->nr_initvals+1,
sizeof(struct ain_initval));
ain->global_initvals[ain->nr_initvals++] = *init;
}
static const char *errtab[AIN_MAX_ERROR] = {
[AIN_SUCCESS] = "Success",
[AIN_FILE_ERROR] = "Error opening AIN file",
+12
View File
@@ -24,6 +24,7 @@
#include "ainedit.h"
#include "system4.h"
#include "system4/ain.h"
#include "jaf.h"
static void usage(void)
{
@@ -32,6 +33,7 @@ static void usage(void)
puts("");
puts(" -h, --help Display this message and exit");
puts(" -c, --code <jam-file> Update the CODE section (assemble .jam file)");
puts(" --jaf <jaf-file> Update AIN file from JAF source code");
puts(" -j, --json <json-file> Update AIN file from JSON data");
puts(" -t, --text <text-file> Update strings/messages");
puts(" --transcode <enc> Change the AIN file's text encoding");
@@ -48,6 +50,7 @@ extern int text_parse(void);
enum {
LOPT_HELP = 256,
LOPT_CODE,
LOPT_JAF,
LOPT_JSON,
LOPT_TEXT,
LOPT_TRANSCODE,
@@ -86,6 +89,7 @@ int main(int argc, char *argv[])
struct ain *ain;
int err = AIN_SUCCESS;
const char *code_file = NULL;
const char *jaf_file = NULL;
const char *decl_file = NULL;
const char *text_file = NULL;
const char *output_file = NULL;
@@ -98,6 +102,7 @@ int main(int argc, char *argv[])
static struct option long_options[] = {
{ "help", no_argument, 0, LOPT_HELP },
{ "code", required_argument, 0, LOPT_CODE },
{ "jaf", required_argument, 0, LOPT_JAF },
{ "json", required_argument, 0, LOPT_JSON },
{ "text", required_argument, 0, LOPT_TEXT },
{ "transcode", required_argument, 0, LOPT_TRANSCODE },
@@ -123,6 +128,9 @@ int main(int argc, char *argv[])
case LOPT_CODE:
code_file = optarg;
break;
case LOPT_JAF:
jaf_file = optarg;
break;
case 'j':
case LOPT_JSON:
decl_file = optarg;
@@ -192,6 +200,10 @@ int main(int argc, char *argv[])
read_declarations(decl_file, ain);
}
if (jaf_file) {
jaf_compile(ain, jaf_file);
}
if (code_file) {
asm_assemble_jam(code_file, ain, flags);
}
+194
View File
@@ -0,0 +1,194 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#ifndef AINEDIT_JAF_AST_H
#define AINEDIT_JAF_AST_H
#include <stdbool.h>
#include <stdio.h>
#include "system4/ain.h"
struct string;
enum jaf_type {
JAF_VOID,
JAF_INT,
JAF_FLOAT,
JAF_STRING,
JAF_STRUCT,
JAF_ENUM,
JAF_TYPEDEF,
};
enum jaf_type_qualifier {
JAF_QUAL_CONST = 1,
JAF_QUAL_REF = 2
};
enum jaf_expression_type {
JAF_EXP_VOID = 0,
JAF_EXP_INT,
JAF_EXP_FLOAT,
JAF_EXP_STRING,
JAF_EXP_IDENTIFIER,
JAF_EXP_UNARY,
JAF_EXP_BINARY,
JAF_EXP_TERNARY,
};
enum jaf_operator {
JAF_NO_OPERATOR = 0,
JAF_AMPERSAND,
JAF_UNARY_PLUS,
JAF_UNARY_MINUS,
JAF_BIT_NOT,
JAF_LOG_NOT,
JAF_PRE_INC,
JAF_PRE_DEC,
JAF_POST_INC,
JAF_POST_DEC,
JAF_MULTIPLY,
JAF_DIVIDE,
JAF_REMAINDER,
JAF_PLUS,
JAF_MINUS,
JAF_LSHIFT,
JAF_RSHIFT,
JAF_LT,
JAF_GT,
JAF_LTE,
JAF_GTE,
JAF_EQ,
JAF_BIT_AND,
JAF_BIT_XOR,
JAF_BIT_IOR,
JAF_LOG_AND,
JAF_LOG_OR,
JAF_ASSIGN,
JAF_MUL_ASSIGN,
JAF_DIV_ASSIGN,
JAF_MOD_ASSIGN,
JAF_ADD_ASSIGN,
JAF_SUB_ASSIGN,
JAF_LSHIFT_ASSIGN,
JAF_RSHIFT_ASSIGN,
JAF_AND_ASSIGN,
JAF_XOR_ASSIGN,
JAF_OR_ASSIGN,
JAF_REF_ASSIGN,
};
struct jaf_expression {
enum jaf_expression_type type;
enum jaf_operator op;
enum jaf_type derived_type;
union {
int i;
float f;
struct string *s;
// unary operators
struct jaf_expression *expr;
// binary operators
struct {
struct jaf_expression *lhs;
struct jaf_expression *rhs;
};
// ternary operator
struct {
struct jaf_expression *condition;
struct jaf_expression *consequent;
struct jaf_expression *alternative;
};
};
};
struct jaf_type_specifier {
enum jaf_type type;
unsigned qualifiers;
struct string *name;
struct jaf_declaration_list *def;
int struct_no;
};
struct jaf_declarator {
struct string *name;
struct jaf_expression *init;
size_t array_rank;
size_t *array_dims;
};
struct jaf_declarator_list {
size_t nr_decls;
struct jaf_declarator **decls;
};
struct jaf_function {
// TODO
};
struct jaf_declaration {
bool function;
struct string *name;
struct jaf_type_specifier *type;
size_t array_rank;
size_t *array_dims;
struct jaf_expression *init;
struct jaf_function *fun;
int var_no;
};
struct jaf_declaration_list {
size_t nr_decls;
struct jaf_declaration **decls;
};
struct jaf_expression *jaf_integer(struct string *text);
struct jaf_expression *jaf_float(struct string *text);
struct jaf_expression *jaf_string(struct string *text);
struct jaf_expression *jaf_identifier(struct string *name);
struct jaf_expression *jaf_unary_expr(enum jaf_operator op, struct jaf_expression *expr);
struct jaf_expression *jaf_binary_expr(enum jaf_operator op, struct jaf_expression *lhs, struct jaf_expression *rhs);
struct jaf_expression *jaf_ternary_expr(struct jaf_expression *test, struct jaf_expression *cons, struct jaf_expression *alt);
struct jaf_expression *jaf_seq_expr(struct jaf_expression *head, struct jaf_expression *tail);
struct jaf_type_specifier *jaf_type(enum jaf_type type);
struct jaf_type_specifier *jaf_struct(struct string *name, struct jaf_declaration_list *fields);
struct jaf_type_specifier *jaf_typedef(struct string *name);
struct jaf_declarator *jaf_declarator(struct string *name);
struct jaf_declarator_list *jaf_declarators(struct jaf_declarator_list *head, struct jaf_declarator *tail);
struct jaf_declaration_list *jaf_declaration(struct jaf_type_specifier *type, struct jaf_declarator_list *declarators);
struct jaf_declaration_list *jaf_type_declaration(struct jaf_type_specifier *type);
struct jaf_declaration_list *jaf_declarations(struct jaf_declaration_list *head, struct jaf_declaration_list *tail);
void jaf_free_expr(struct jaf_expression *expr);
// jaf_compile.c
struct ain *jaf_ain_out;
struct jaf_declaration_list *jaf_toplevel;
void jaf_compile(struct ain *out, const char *path);
void jaf_define_struct(struct ain *ain, struct jaf_type_specifier *type);
// jaf_eval.c
struct jaf_expression *jaf_simplify(struct jaf_expression *in);
struct jaf_expression *jaf_compute_constexpr(struct jaf_expression *in);
// jaf_types.c
void jaf_check_types(struct jaf_expression *expr, struct jaf_type_specifier *type);
#endif /* AINEDIT_JAF_AST_H */
+200
View File
@@ -0,0 +1,200 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "system4.h"
#include "system4/string.h"
#include "jaf.h"
static struct jaf_expression *jaf_expr(enum jaf_expression_type type, enum jaf_operator op)
{
struct jaf_expression *e = xcalloc(1, sizeof(struct jaf_expression));
e->type = type;
e->op = op;
return e;
}
struct jaf_expression *jaf_integer(struct string *text)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_INT, 0);
char *endptr;
errno = 0;
e->i = strtol(text->text, &endptr, 0);
if (errno || *endptr != '\0')
ERROR("Invalid integer constant");
free_string(text);
return e;
}
struct jaf_expression *jaf_float(struct string *text)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_FLOAT, 0);
char *endptr;
errno = 0;
e->f = strtof(text->text, &endptr);
if (errno || *endptr != '\0')
ERROR("Invalid floating point constant");
free_string(text);
return e;
}
struct jaf_expression *jaf_string(struct string *text)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_STRING, 0);
e->s = text;
return e;
}
struct jaf_expression *jaf_identifier(struct string *name)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_IDENTIFIER, 0);
e->s = name;
return e;
}
struct jaf_expression *jaf_unary_expr(enum jaf_operator op, struct jaf_expression *expr)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_UNARY, op);
e->expr = expr;
return e;
}
struct jaf_expression *jaf_binary_expr(enum jaf_operator op, struct jaf_expression *lhs, struct jaf_expression *rhs)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_BINARY, op);
e->lhs = lhs;
e->rhs = rhs;
return e;
}
struct jaf_expression *jaf_ternary_expr(struct jaf_expression *test, struct jaf_expression *cons, struct jaf_expression *alt)
{
struct jaf_expression *e = jaf_expr(JAF_EXP_TERNARY, 0);
e->condition = test;
e->consequent = cons;
e->alternative = alt;
return e;
}
struct jaf_expression *jaf_seq_expr(struct jaf_expression *head, struct jaf_expression *tail)
{
ERROR("Sequence expressions not supported");
}
struct jaf_type_specifier *jaf_type(enum jaf_type type)
{
struct jaf_type_specifier *p = xcalloc(1, sizeof(struct jaf_type_specifier));
p->type = type;
p->struct_no = -1;
return p;
}
struct jaf_type_specifier *jaf_struct(struct string *name, struct jaf_declaration_list *fields)
{
struct jaf_type_specifier *p = jaf_type(JAF_STRUCT);
p->name = name;
p->def = fields;
return p;
}
struct jaf_type_specifier *jaf_typedef(struct string *name)
{
struct jaf_type_specifier *p = jaf_type(JAF_TYPEDEF);
p->name = name;
return p;
}
struct jaf_declarator *jaf_declarator(struct string *name)
{
struct jaf_declarator *d = xcalloc(1, sizeof(struct jaf_declarator));
d->name = name;
return d;
}
struct jaf_declarator_list *jaf_declarators(struct jaf_declarator_list *head, struct jaf_declarator *tail)
{
if (!head) {
head = xcalloc(1, sizeof(struct jaf_declarator_list));
}
head->decls = xrealloc_array(head->decls, head->nr_decls, head->nr_decls+1, sizeof(struct jaf_declarator*));
head->decls[head->nr_decls++] = tail;
return head;
}
struct jaf_declaration_list *jaf_declaration(struct jaf_type_specifier *type, struct jaf_declarator_list *declarators)
{
struct jaf_declaration_list *decls = xcalloc(1, sizeof(struct jaf_declaration_list));
decls->nr_decls = declarators->nr_decls;
decls->decls = xcalloc(declarators->nr_decls, sizeof(struct jaf_declaration*));
for (size_t i = 0; i < declarators->nr_decls; i++) {
struct jaf_declaration *decl = xcalloc(1, sizeof(struct jaf_declaration));
decl->function = false;
decl->name = declarators->decls[i]->name;
decl->type = type;
decl->init = declarators->decls[i]->init;
decls->decls[i] = decl;
free(declarators->decls[i]);
}
free(declarators);
return decls;
}
struct jaf_declaration_list *jaf_type_declaration(struct jaf_type_specifier *type)
{
struct jaf_declaration_list *decls = xcalloc(1, sizeof(struct jaf_declaration_list));
decls->nr_decls = 1;
decls->decls = xcalloc(1, sizeof(struct jaf_declaration*));
decls->decls[0] = xcalloc(1, sizeof(struct jaf_declaration));
decls->decls[0]->type = type;
return decls;
}
struct jaf_declaration_list *jaf_declarations(struct jaf_declaration_list *head, struct jaf_declaration_list *tail)
{
if (!head)
return tail;
size_t nr_decls = head->nr_decls + tail->nr_decls;
head->decls = xrealloc_array(head->decls, head->nr_decls, nr_decls, sizeof(struct jaf_toplevel_decl*));
for (size_t i = 0; i < tail->nr_decls; i++) {
head->decls[head->nr_decls+i] = tail->decls[i];
}
head->nr_decls = nr_decls;
free(tail);
return head;
}
void jaf_free_expr(struct jaf_expression *expr)
{
if (expr->type == JAF_EXP_UNARY) {
jaf_free_expr(expr->expr);
} else if (expr->type == JAF_EXP_BINARY) {
jaf_free_expr(expr->lhs);
jaf_free_expr(expr->rhs);
} else if (expr->type == JAF_EXP_TERNARY) {
jaf_free_expr(expr->condition);
jaf_free_expr(expr->consequent);
jaf_free_expr(expr->alternative);
}
free(expr);
}
+236
View File
@@ -0,0 +1,236 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "system4.h"
#include "system4/ain.h"
#include "system4/string.h"
#include "ainedit.h"
#include "jaf.h"
#include "jaf_parser.tab.h"
FILE *yyin;
int yyparse(void);
struct ain *jaf_ain_out;
struct jaf_declaration_list *jaf_toplevel;
int sym_type(char *name)
{
char *u = encode_text_to_input_format(name);
struct ain_struct *s = ain_get_struct(jaf_ain_out, u);
free(u);
if (s)
return TYPEDEF_NAME;
return IDENTIFIER;
}
static void define_types(struct ain *ain, struct jaf_declaration *decl);
void jaf_define_struct(struct ain *ain, struct jaf_type_specifier *type)
{
if (!type->name)
ERROR("Anonymous structs not supported");
char *u = encode_text_to_input_format(type->name->text);
if (ain_get_struct(ain, u)) {
ERROR("Redefining structs not supported");
}
type->struct_no = ain_add_struct(ain, u);
free(u);
for (size_t i = 0; i < type->def->nr_decls; i++) {
define_types(ain, type->def->decls[i]);
}
}
static void define_types(struct ain *ain, struct jaf_declaration *decl)
{
if (!decl)
ERROR("DECL IS NULL");
if (!decl->type)
ERROR("TYPE IS NULL");
if (decl->type->type != JAF_STRUCT)
return;
jaf_define_struct(ain, decl->type);
}
static enum ain_data_type jaf_to_ain_data_type(enum jaf_type type)
{
switch (type) {
case JAF_VOID: return AIN_VOID;
case JAF_INT: return AIN_INT;
case JAF_FLOAT: return AIN_FLOAT;
case JAF_STRING: return AIN_STRING;
case JAF_STRUCT: return AIN_STRUCT;
case JAF_ENUM: ERROR("Enums not supported");
default: ERROR("Unknown type: %d", type);
}
}
static void jaf_to_ain_type(possibly_unused struct ain *ain, struct ain_type *out, struct jaf_type_specifier *in)
{
out->data = jaf_to_ain_data_type(in->type);
if (in->type == JAF_STRUCT) {
out->struc = in->struct_no;
}
}
static void resolve_typedef(struct ain *ain, struct jaf_type_specifier *type)
{
char *u = encode_text_to_input_format(type->name->text);
int sno = ain_get_struct_no(ain, u);
if (sno) {
type->type = JAF_STRUCT;
type->struct_no = sno;
} else {
ERROR("Failed to resolve typedef \"%s\"", type->name->text);
}
free(u);
}
static void jaf_to_initval(struct ain_initval *dst, struct jaf_expression *expr)
{
switch (expr->type) {
case JAF_EXP_INT:
dst->data_type = AIN_INT;
dst->int_value = expr->i;
break;
case JAF_EXP_FLOAT:
dst->data_type = AIN_FLOAT;
dst->float_value = expr->f;
break;
case JAF_EXP_STRING:
dst->data_type = AIN_STRING;
dst->string_value = strdup(expr->s->text);
break;
default:
ERROR("Initval is not constant");
}
}
static void analyze_expression(possibly_unused struct ain *ain, struct jaf_expression **expr, struct jaf_type_specifier *type)
{
jaf_check_types(*expr, type);
*expr = jaf_simplify(*expr);
}
static void resolve_types(struct ain *ain, struct jaf_declaration *decl)
{
if (decl->function)
return;
if (decl->type->type == JAF_TYPEDEF)
resolve_typedef(ain, decl->type);
if (decl->type->type != JAF_STRUCT || !decl->type->def)
return;
assert(decl->type->struct_no >= 0); // set in parse phase
assert(decl->type->struct_no < ain->nr_structures);
// create struct definition in ain object
struct jaf_declaration_list *def = decl->type->def;
struct ain_variable *members = xcalloc(def->nr_decls, sizeof(struct ain_variable));
for (size_t i = 0; i < def->nr_decls; i++) {
members[i].name = encode_text_to_input_format(def->decls[i]->name->text);
if (ain->version >= 12)
members[i].name2 = strdup("");
jaf_to_ain_type(ain, &members[i].type, def->decls[i]->type);
}
struct ain_struct *s = &ain->structures[decl->type->struct_no];
s->nr_members = def->nr_decls;
s->members = members;
}
static void add_global(struct ain *ain, struct jaf_declaration *decl)
{
char *u = encode_text_to_input_format(decl->name->text);
struct ain_variable *v = ain_add_global(ain, u);
jaf_to_ain_type(ain, &v->type, decl->type);
decl->var_no = v - ain->globals;
free(u);
}
static void compile_declaration(possibly_unused struct ain *ain, struct jaf_declaration *decl)
{
if (decl->function) {
WARNING("Functions not supported");
return;
}
// TODO: global constructors/array allocations
}
void jaf_compile(struct ain *out, const char *path)
{
if (path) {
if (!strcmp(path, "-"))
yyin = stdin;
else
yyin = fopen(path, "rb");
if (!yyin)
ERROR("Opening input file '%s': %s", path, strerror(errno));
}
jaf_ain_out = out;
yyparse();
if (!jaf_toplevel)
ERROR("Failed to parse .jaf file");
// pass 1: typedefs && struct definitions
for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) {
resolve_types(out, jaf_toplevel->decls[i]);
}
// pass 2: register globals (names, types)
for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) {
if (jaf_toplevel->decls[i]->name) {
if (jaf_toplevel->decls[i]->function)
ERROR("Functions not supported");
else
add_global(out, jaf_toplevel->decls[i]);
}
}
// pass 3: type analysis & simplification & global initvals (static analysis)
for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) {
if (jaf_toplevel->decls[i]->init) {
analyze_expression(out, &jaf_toplevel->decls[i]->init, jaf_toplevel->decls[i]->type);
// add initval to ain object
struct ain_initval init = { .global_index = jaf_toplevel->decls[i]->var_no };
jaf_to_initval(&init, jaf_toplevel->decls[i]->init);
ain_add_initval(out, &init);
}
}
// pass 4: generate bytecode
for (size_t i = 0; i < jaf_toplevel->nr_decls; i++) {
compile_declaration(out, jaf_toplevel->decls[i]);
}
// XXX: ain_add_initval adds initval to GSET section of the ain file.
// If the original ain file did not have a GSET section, init
// code needs to be added to the "0" function instead.
if (out->nr_initvals && !out->GSET.present) {
WARNING("%d global initvals ignored", out->nr_initvals);
}
}
+257
View File
@@ -0,0 +1,257 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "system4.h"
#include "system4/string.h"
#include "jaf.h"
struct jaf_expression *jaf_simplify(struct jaf_expression *in);
static struct jaf_expression *jaf_simplify_negation(struct jaf_expression *expr)
{
if (expr->type == JAF_EXP_INT) {
expr->i = -expr->i;
return expr;
}
if (expr->type == JAF_EXP_FLOAT) {
expr->f = -expr->f;
return expr;
}
return jaf_unary_expr(JAF_UNARY_MINUS, expr);
}
static struct jaf_expression *jaf_simplify_bitnot(struct jaf_expression *expr)
{
if (expr->type == JAF_EXP_INT) {
expr->i = ~expr->i;
return expr;
}
return jaf_unary_expr(JAF_BIT_NOT, expr);
}
static struct jaf_expression *jaf_simplify_lognot(struct jaf_expression *expr)
{
if (expr->type == JAF_EXP_INT) {
expr->i = !expr->i;
return expr;
}
return jaf_unary_expr(JAF_LOG_NOT, expr);
}
static struct jaf_expression *jaf_simplify_unary(struct jaf_expression *in)
{
struct jaf_expression *out = jaf_simplify(in->expr);
switch (in->op) {
case JAF_UNARY_PLUS:
return out;
case JAF_UNARY_MINUS:
return jaf_simplify_negation(out);
case JAF_BIT_NOT:
return jaf_simplify_bitnot(out);
case JAF_LOG_NOT:
return jaf_simplify_lognot(out);
case JAF_AMPERSAND:
case JAF_PRE_INC:
case JAF_PRE_DEC:
case JAF_POST_INC:
case JAF_POST_DEC:
return jaf_unary_expr(in->op, out);
default:
break;
}
ERROR("Invalid unary operator");
}
static void jaf_normalize_for_arithmetic(struct jaf_expression *lhs, struct jaf_expression *rhs)
{
if (lhs->type == JAF_EXP_FLOAT && rhs->type == JAF_EXP_INT) {
rhs->type = JAF_EXP_FLOAT;
rhs->f = rhs->i;
} else if (lhs->type == JAF_EXP_INT && rhs->type == JAF_EXP_FLOAT) {
lhs->type = JAF_EXP_FLOAT;
lhs->f = lhs->i;
}
}
#define SIMPLIFY_ARITHMETIC_FUN(name, op_name, op) \
static struct jaf_expression *name(struct jaf_expression *lhs, struct jaf_expression *rhs) \
{ \
jaf_normalize_for_arithmetic(lhs, rhs); \
if (lhs->type == JAF_EXP_INT && rhs->type == JAF_EXP_INT) { \
lhs->i = lhs->i op rhs->i; \
free(rhs); \
return lhs; \
} \
if (lhs->type == JAF_EXP_FLOAT && rhs->type == JAF_EXP_FLOAT) { \
lhs->f = lhs->f op rhs->f; \
free(rhs); \
return lhs; \
} \
return jaf_binary_expr(op_name, lhs, rhs); \
}
#define SIMPLIFY_INTEGER_FUN(name, op_name, op) \
static struct jaf_expression *name(struct jaf_expression *lhs, struct jaf_expression *rhs) \
{ \
jaf_normalize_for_arithmetic(lhs, rhs); \
if (lhs->type == JAF_EXP_INT && rhs->type == JAF_EXP_INT) { \
lhs->i = lhs->i op rhs->i; \
free(rhs); \
return lhs; \
} \
return jaf_binary_expr(op_name, lhs, rhs); \
}
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_multiply, JAF_MULTIPLY, *)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_divide, JAF_DIVIDE, /)
SIMPLIFY_INTEGER_FUN (jaf_simplify_remainder, JAF_REMAINDER, %)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_plus, JAF_PLUS, +) // TODO: string concatentation
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_minus, JAF_MINUS, -)
SIMPLIFY_INTEGER_FUN (jaf_simplify_lshift, JAF_LSHIFT, <<)
SIMPLIFY_INTEGER_FUN (jaf_simplify_rshift, JAF_RSHIFT, >>)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_lt, JAF_LT, <)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_gt, JAF_GT, >)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_lte, JAF_LTE, <=)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_gte, JAF_GTE, >=)
SIMPLIFY_ARITHMETIC_FUN(jaf_simplify_eq, JAF_EQ, ==)
SIMPLIFY_INTEGER_FUN (jaf_simplify_bitand, JAF_BIT_AND, &)
SIMPLIFY_INTEGER_FUN (jaf_simplify_bitxor, JAF_BIT_XOR, ^)
SIMPLIFY_INTEGER_FUN (jaf_simplify_bitior, JAF_BIT_IOR, |)
SIMPLIFY_INTEGER_FUN (jaf_simplify_logand, JAF_LOG_AND, &&)
SIMPLIFY_INTEGER_FUN (jaf_simplify_logor, JAF_LOG_OR, ||)
static struct jaf_expression *jaf_simplify_binary(struct jaf_expression *in)
{
enum jaf_operator op = in->op;
struct jaf_expression *lhs = jaf_simplify(in->lhs);
struct jaf_expression *rhs = jaf_simplify(in->rhs);
free(in);
switch (op) {
case JAF_MULTIPLY:
return jaf_simplify_multiply(lhs, rhs);
case JAF_DIVIDE:
return jaf_simplify_divide(lhs, rhs);
case JAF_REMAINDER:
return jaf_simplify_remainder(lhs, rhs);
case JAF_PLUS:
return jaf_simplify_plus(lhs, rhs);
case JAF_MINUS:
return jaf_simplify_minus(lhs, rhs);
case JAF_LSHIFT:
return jaf_simplify_lshift(lhs, rhs);
case JAF_RSHIFT:
return jaf_simplify_rshift(lhs, rhs);
case JAF_LT:
return jaf_simplify_lt(lhs, rhs);
case JAF_GT:
return jaf_simplify_gt(lhs, rhs);
case JAF_LTE:
return jaf_simplify_lte(lhs, rhs);
case JAF_GTE:
return jaf_simplify_gte(lhs, rhs);
case JAF_EQ:
return jaf_simplify_eq(lhs, rhs);
case JAF_BIT_AND:
return jaf_simplify_bitand(lhs, rhs);
case JAF_BIT_XOR:
return jaf_simplify_bitxor(lhs, rhs);
case JAF_BIT_IOR:
return jaf_simplify_bitior(lhs, rhs);
case JAF_LOG_AND:
return jaf_simplify_logand(lhs, rhs);
case JAF_LOG_OR:
return jaf_simplify_logor(lhs, rhs);
case JAF_ASSIGN:
case JAF_MUL_ASSIGN:
case JAF_DIV_ASSIGN:
case JAF_MOD_ASSIGN:
case JAF_ADD_ASSIGN:
case JAF_SUB_ASSIGN:
case JAF_LSHIFT_ASSIGN:
case JAF_RSHIFT_ASSIGN:
case JAF_AND_ASSIGN:
case JAF_XOR_ASSIGN:
case JAF_OR_ASSIGN:
case JAF_REF_ASSIGN:
return jaf_binary_expr(op, lhs, rhs);
default:
ERROR("Invalid binary operator");
}
}
struct jaf_expression *jaf_simplify_ternary(struct jaf_expression *in)
{
in->condition = jaf_simplify(in->condition);
in->consequent = jaf_simplify(in->consequent);
in->alternative = jaf_simplify(in->alternative);
if (in->condition->type == JAF_EXP_INT) {
if (in->condition->i) {
jaf_free_expr(in->condition);
jaf_free_expr(in->alternative);
free(in);
return in->consequent;
} else {
jaf_free_expr(in->condition);
jaf_free_expr(in->consequent);
free(in);
return in->alternative;
}
}
return in;
}
/*
* Simplify an expression by evaluating the constant parts.
*/
struct jaf_expression *jaf_simplify(struct jaf_expression *in)
{
switch (in->type) {
case JAF_EXP_VOID:
case JAF_EXP_INT:
case JAF_EXP_FLOAT:
case JAF_EXP_STRING:
case JAF_EXP_IDENTIFIER:
return in;
case JAF_EXP_UNARY:
return jaf_simplify_unary(in);
case JAF_EXP_BINARY:
return jaf_simplify_binary(in);
case JAF_EXP_TERNARY:
return jaf_simplify_ternary(in);
}
ERROR("Invalid expression type");
}
struct jaf_expression *jaf_compute_constexpr(struct jaf_expression *in)
{
struct jaf_expression *out = jaf_simplify(in);
switch (out->type) {
case JAF_EXP_INT:
case JAF_EXP_FLOAT:
case JAF_EXP_STRING:
return out;
default:
return NULL;
}
}
+193
View File
@@ -0,0 +1,193 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*
* Based on ANSI C grammar from http://www.quut.com/c/ANSI-C-grammar-l-2011.html
*/
%e 1019
%p 2807
%n 371
%k 284
%a 1213
%o 1117
O [0-7]
D [0-9]
NZ [1-9]
L [a-zA-Z_]
A [a-zA-Z_0-9]
H [a-fA-F0-9]
HP (0[xX])
E ([Ee][+-]?{D}+)
P ([Pp][+-]?{D}+)
FS (f|F|l|L)
IS (((u|U)(l|L|ll|LL)?)|((l|L|ll|LL)(u|U)?))
CP (u|U|L)
SP (u8|u|U|L)
ES (\\(['"\?\\abfnrtv]|[0-7]{1,3}|x[a-fA-F0-9]+))
WS [ \t\v\n\f]
%{
#pragma GCC diagnostic ignored "-Wunused-function"
#include <stdio.h>
#include "system4/string.h"
#include "jaf_parser.tab.h"
extern void yyerror(const char *); /* prints grammar violation message */
extern int sym_type(const char *); /* returns type from symbol table */
#define sym_type(identifier) IDENTIFIER /* with no symbol table, fake it */
static void comment(void);
static int check_type(void);
#define RETURN_STRING(tok_type) yylval.string = make_string(yytext, yyleng); return tok_type
%}
%%
"/*" { comment(); }
"//".* { /* consume //-comment */ }
"break" { return(BREAK); }
"case" { return(CASE); }
"char" { return(CHAR); }
"const" { return(CONST); }
"continue" { return(CONTINUE); }
"default" { return(DEFAULT); }
"do" { return(DO); }
"else" { return(ELSE); }
"enum" { return(ENUM); }
"float" { return(FLOAT); }
"for" { return(FOR); }
"goto" { return(GOTO); }
"if" { return(IF); }
"int" { return(INT); }
"long" { return(LONG); }
"return" { return(SYM_RETURN); }
"string" { return(STRING); }
"struct" { return(STRUCT); }
"switch" { return(SYM_SWITCH); }
"union" { return(UNION); }
"void" { return(VOID); }
"while" { return(WHILE); }
"bool" { return BOOL; }
"__func__" { return FUNC_NAME; }
"ref" { return SYM_REF; }
{L}{A}* { return check_type(); }
{HP}{H}+{IS}? { RETURN_STRING(I_CONSTANT); }
{NZ}{D}*{IS}? { RETURN_STRING(I_CONSTANT); }
"0"{O}*{IS}? { RETURN_STRING(I_CONSTANT); }
{CP}?"'"([^'\\\n]|{ES})+"'" { RETURN_STRING(I_CONSTANT); }
{D}+{E}{FS}? { RETURN_STRING(F_CONSTANT); }
{D}*"."{D}+{E}?{FS}? { RETURN_STRING(F_CONSTANT); }
{D}+"."{E}?{FS}? { RETURN_STRING(F_CONSTANT); }
{HP}{H}+{P}{FS}? { RETURN_STRING(F_CONSTANT); }
{HP}{H}*"."{H}+{P}{FS}? { RETURN_STRING(F_CONSTANT); }
{HP}{H}+"."{P}{FS}? { RETURN_STRING(F_CONSTANT); }
({SP}?\"([^"\\\n]|{ES})*\"{WS}*)+ { RETURN_STRING(STRING_LITERAL); }
"..." { return ELLIPSIS; }
">>=" { return RIGHT_ASSIGN; }
"<<=" { return LEFT_ASSIGN; }
"+=" { return ADD_ASSIGN; }
"-=" { return SUB_ASSIGN; }
"*=" { return MUL_ASSIGN; }
"/=" { return DIV_ASSIGN; }
"%=" { return MOD_ASSIGN; }
"&=" { return AND_ASSIGN; }
"^=" { return XOR_ASSIGN; }
"|=" { return OR_ASSIGN; }
"<-" { return REF_ASSIGN; }
">>" { return RIGHT_OP; }
"<<" { return LEFT_OP; }
"++" { return INC_OP; }
"--" { return DEC_OP; }
"&&" { return AND_OP; }
"||" { return OR_OP; }
"<=" { return LE_OP; }
">=" { return GE_OP; }
"==" { return EQ_OP; }
"!=" { return NE_OP; }
";" { return ';'; }
("{"|"<%") { return '{'; }
("}"|"%>") { return '}'; }
"," { return ','; }
":" { return ':'; }
"=" { return '='; }
"(" { return '('; }
")" { return ')'; }
("["|"<:") { return '['; }
("]"|":>") { return ']'; }
"." { return '.'; }
"&" { return '&'; }
"!" { return '!'; }
"~" { return '~'; }
"-" { return '-'; }
"+" { return '+'; }
"*" { return '*'; }
"/" { return '/'; }
"%" { return '%'; }
"<" { return '<'; }
">" { return '>'; }
"^" { return '^'; }
"|" { return '|'; }
"?" { return '?'; }
{WS}+ { /* whitespace separates tokens */ }
. { /* discard bad characters */ }
%%
int yywrap(void) /* called at end of input */
{
return 1; /* terminate now */
}
static void comment(void)
{
int c;
while ((c = input()) != 0)
if (c == '*')
{
while ((c = input()) == '*')
;
if (c == '/')
return;
if (c == 0)
break;
}
yyerror("unterminated comment");
}
static int check_type(void)
{
switch (sym_type(yytext))
{
case TYPEDEF_NAME: /* previously defined */
RETURN_STRING(TYPEDEF_NAME);
case ENUMERATION_CONSTANT: /* previously defined */
RETURN_STRING(ENUMERATION_CONSTANT);
default: /* includes undefined */
RETURN_STRING(IDENTIFIER);
}
}
+452
View File
@@ -0,0 +1,452 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*
* Based on ANSI C grammar from http://www.quut.com/c/ANSI-C-grammar-l-2011.html
*/
%{
#include "system4.h"
#include "system4/ain.h"
#include "jaf.h"
extern int yylex();
void yyerror(const char *s);
%}
%union {
int token;
struct string *string;
struct jaf_expression *expression;
struct jaf_type_specifier *type;
struct jaf_declarator *declarator;
struct jaf_declarator_list *declarators;
struct jaf_declaration_list *declarations;
}
%token <string> I_CONSTANT F_CONSTANT STRING_LITERAL
%token <string> IDENTIFIER TYPEDEF_NAME ENUMERATION_CONSTANT
%token <token> INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
%token <token> AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
%token <token> SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
%token <token> XOR_ASSIGN OR_ASSIGN
%token <token> SYM_REF REF_ASSIGN FUNC_NAME
%token <token> CONST
%token <token> BOOL CHAR INT LONG FLOAT VOID STRING
%token <token> STRUCT UNION ENUM ELLIPSIS
%token CASE DEFAULT IF ELSE SYM_SWITCH WHILE DO FOR GOTO CONTINUE BREAK SYM_RETURN
%type <token> unary_operator assignment_operator type_qualifier
%type <string> string param_identifer
%type <expression> initializer
%type <expression> postfix_expression unary_expression cast_expression
%type <expression> multiplicative_expression additive_expression shift_expression
%type <expression> relational_expression equality_expression and_expression
%type <expression> exclusive_or_expression inclusive_or_expression
%type <expression> logical_and_expression logical_or_expression
%type <expression> conditional_expression assignment_expression
%type <expression> expression constant_expression primary_expression constant
%type <type> type_specifier struct_specifier declaration_specifiers
%type <declarator> init_declarator declarator
%type <declarators> init_declarator_list struct_declarator_list
%type <declarations> translation_unit external_declaration declaration
%type <declarations> struct_declaration struct_declaration_list
/*
* Shift-reduce conflicts:
* 1. dangling else
*/
%expect 1
%start toplevel
%%
primary_expression
: IDENTIFIER { $$ = jaf_identifier($1); }
| constant { $$ = $1; }
| string { $$ = jaf_string($1); }
| '(' expression ')' { $$ = $2; }
;
constant
: I_CONSTANT { $$ = jaf_integer($1); } /* includes character_constant */
| F_CONSTANT { $$ = jaf_float($1); }
| ENUMERATION_CONSTANT { ERROR("Enums not supported"); } /* after it has been defined as such */
;
enumeration_constant /* before it has been defined as such */
: IDENTIFIER
;
string
: STRING_LITERAL { $$ = $1; }
| FUNC_NAME { ERROR("__func__ not supported"); }
// | STRING '(' assignment_expression ')'
;
postfix_expression
: primary_expression { $$ = $1; }
| postfix_expression '[' expression ']' { ERROR("Arrays not supported"); }
| postfix_expression '(' ')' { ERROR("Function calls not supported"); }
| type_specifier '(' expression ')' { ERROR("Cast not supported"); }
| postfix_expression '(' argument_expression_list ')' { ERROR("Function calls not supported"); }
| postfix_expression '.' IDENTIFIER { ERROR("Struct members not supported"); }
| postfix_expression INC_OP { $$ = jaf_unary_expr(JAF_POST_INC, $1); }
| postfix_expression DEC_OP { $$ = jaf_unary_expr(JAF_POST_DEC, $1); }
// | '(' type_specifier ')' '{' initializer_list '}'
// | '(' type_specifier ')' '{' initializer_list ',' '}'
;
argument_expression_list
: assignment_expression
| argument_expression_list ',' assignment_expression
;
unary_expression
: postfix_expression { $$ = $1; }
| INC_OP unary_expression { $$ = jaf_unary_expr(JAF_PRE_INC, $2); }
| DEC_OP unary_expression { $$ = jaf_unary_expr(JAF_PRE_DEC, $2); }
| unary_operator cast_expression { $$ = jaf_unary_expr($1, $2); }
;
unary_operator
: '&' { $$ = JAF_AMPERSAND; }
| '+' { $$ = JAF_UNARY_PLUS; }
| '-' { $$ = JAF_UNARY_MINUS; }
| '~' { $$ = JAF_BIT_NOT; }
| '!' { $$ = JAF_LOG_NOT; }
;
cast_expression
: unary_expression { $$ = $1; }
| '(' type_specifier ')' cast_expression { ERROR("Cast not supported"); }
;
multiplicative_expression
: cast_expression { $$ = $1; }
| multiplicative_expression '*' cast_expression { $$ = jaf_binary_expr(JAF_MULTIPLY, $1, $3); }
| multiplicative_expression '/' cast_expression { $$ = jaf_binary_expr(JAF_DIVIDE, $1, $3); }
| multiplicative_expression '%' cast_expression { $$ = jaf_binary_expr(JAF_REMAINDER, $1, $3); }
;
additive_expression
: multiplicative_expression { $$ = $1; }
| additive_expression '+' multiplicative_expression { $$ = jaf_binary_expr(JAF_PLUS, $1, $3); }
| additive_expression '-' multiplicative_expression { $$ = jaf_binary_expr(JAF_MINUS, $1, $3); }
;
shift_expression
: additive_expression { $$ = $1; }
| shift_expression LEFT_OP additive_expression { $$ = jaf_binary_expr(JAF_LSHIFT, $1, $3); }
| shift_expression RIGHT_OP additive_expression { $$ = jaf_binary_expr(JAF_RSHIFT, $1, $3); }
;
relational_expression
: shift_expression { $$ = $1; }
| relational_expression '<' shift_expression { $$ = jaf_binary_expr(JAF_LT, $1, $3); }
| relational_expression '>' shift_expression { $$ = jaf_binary_expr(JAF_GT, $1, $3); }
| relational_expression LE_OP shift_expression { $$ = jaf_binary_expr(JAF_LTE, $1, $3); }
| relational_expression GE_OP shift_expression { $$ = jaf_binary_expr(JAF_GTE, $1, $3); }
;
equality_expression
: relational_expression { $$ = $1; }
| equality_expression EQ_OP relational_expression { $$ = jaf_binary_expr(JAF_EQ, $1, $3); }
| equality_expression NE_OP relational_expression { $$ = jaf_unary_expr(JAF_LOG_NOT, jaf_binary_expr(JAF_EQ, $1, $3)); }
;
and_expression
: equality_expression { $$ = $1; }
| and_expression '&' equality_expression { $$ = jaf_binary_expr(JAF_BIT_AND, $1, $3); }
;
exclusive_or_expression
: and_expression { $$ = $1; }
| exclusive_or_expression '^' and_expression { $$ = jaf_binary_expr(JAF_BIT_XOR, $1, $3); }
;
inclusive_or_expression
: exclusive_or_expression { $$ = $1; }
| inclusive_or_expression '|' exclusive_or_expression { $$ = jaf_binary_expr(JAF_BIT_IOR, $1, $3); }
;
logical_and_expression
: inclusive_or_expression { $$ = $1; }
| logical_and_expression AND_OP inclusive_or_expression { $$ = jaf_binary_expr(JAF_LOG_AND, $1, $3); }
;
logical_or_expression
: logical_and_expression { $$ = $1; }
| logical_or_expression OR_OP logical_and_expression { $$ = jaf_binary_expr(JAF_LOG_OR, $1, $3); }
;
conditional_expression
: logical_or_expression { $$ = $1; }
| logical_or_expression '?' expression ':' conditional_expression { $$ = jaf_ternary_expr($1, $3, $5); }
;
assignment_expression
: conditional_expression { $$ = $1; }
| unary_expression assignment_operator assignment_expression { $$ = jaf_binary_expr($2, $1, $3); }
;
assignment_operator
: '=' { $$ = JAF_ASSIGN; }
| MUL_ASSIGN { $$ = JAF_MUL_ASSIGN; }
| DIV_ASSIGN { $$ = JAF_DIV_ASSIGN; }
| MOD_ASSIGN { $$ = JAF_MOD_ASSIGN; }
| ADD_ASSIGN { $$ = JAF_ADD_ASSIGN; }
| SUB_ASSIGN { $$ = JAF_SUB_ASSIGN; }
| LEFT_ASSIGN { $$ = JAF_LSHIFT_ASSIGN; }
| RIGHT_ASSIGN { $$ = JAF_RSHIFT_ASSIGN; }
| AND_ASSIGN { $$ = JAF_AND_ASSIGN; }
| XOR_ASSIGN { $$ = JAF_XOR_ASSIGN; }
| OR_ASSIGN { $$ = JAF_OR_ASSIGN; }
| REF_ASSIGN { $$ = JAF_REF_ASSIGN; }
;
expression
: assignment_expression { $$ = $1; }
| expression ',' assignment_expression { $$ = jaf_seq_expr($1, $3); }
;
constant_expression
: conditional_expression { $$ = $1; } /* with constraints */
;
declaration
: declaration_specifiers ';' { $$ = jaf_type_declaration($1); }
| declaration_specifiers init_declarator_list ';' { $$ = jaf_declaration($1, $2); }
;
declaration_specifiers
: type_qualifier type_specifier { $$ = $2; $$->qualifiers = $1; }
| type_specifier { $$ = $1; }
;
init_declarator_list
: init_declarator { $$ = jaf_declarators(NULL, $1); }
| init_declarator_list ',' init_declarator { $$ = jaf_declarators($1, $3); }
;
init_declarator
: declarator '=' initializer { $$ = $1; $$->init = $3; }
| declarator { $$ = $1; }
;
type_specifier
: VOID { $$ = jaf_type(JAF_VOID); }
| CHAR { $$ = jaf_type(JAF_INT); }
| INT { $$ = jaf_type(JAF_INT); }
| LONG { $$ = jaf_type(JAF_INT); }
| FLOAT { $$ = jaf_type(JAF_FLOAT); }
| BOOL { $$ = jaf_type(JAF_INT); }
| STRING { $$ = jaf_type(JAF_STRING); }
| struct_specifier { $$ = $1; }
| enum_specifier { ERROR("Enums not supported"); }
| TYPEDEF_NAME { $$ = jaf_typedef($1); } /* after it has been defined as such */
;
struct_specifier
: STRUCT param_identifer '{' struct_declaration_list '}' { $$ = jaf_struct($2, $4); jaf_define_struct(jaf_ain_out, $$); }
;
param_identifer
: IDENTIFIER { $$ = $1; }
| IDENTIFIER '<' type_parameter_list '>' { ERROR("Type parameters not supported"); }
;
type_parameter_list
: type_specifier
| type_parameter_list ',' type_specifier
;
struct_declaration_list
: struct_declaration { $$ = $1; }
| struct_declaration_list struct_declaration { $$ = jaf_declarations($1, $2); }
;
struct_declaration
// : type_specifier ';' /* for anonymous struct/union */
: type_specifier struct_declarator_list ';' { $$ = jaf_declaration($1, $2); }
;
struct_declarator_list
: declarator { $$ = jaf_declarators(NULL, $1); }
| struct_declarator_list ',' declarator { $$ = jaf_declarators($1, $3); }
;
enum_specifier
: ENUM '{' enumerator_list '}'
| ENUM '{' enumerator_list ',' '}'
| ENUM IDENTIFIER '{' enumerator_list '}'
| ENUM IDENTIFIER '{' enumerator_list ',' '}'
;
enumerator_list
: enumerator
| enumerator_list ',' enumerator
;
enumerator /* identifiers must be flagged as ENUMERATION_CONSTANT */
: enumeration_constant '=' constant_expression
| enumeration_constant
;
type_qualifier
: CONST { $$ = JAF_QUAL_CONST; }
| SYM_REF { $$ = JAF_QUAL_REF; }
;
declarator
: IDENTIFIER { $$ = jaf_declarator($1); }
// | '(' declarator ')' // XXX: causes shift-reduce conflict with function-like casts (e.g. "string(identifier)")
| array_allocation { ERROR("Arrays not supported"); }
| IDENTIFIER '(' parameter_type_list ')' { ERROR("Function declarator not supported"); } // ??? remove?
| IDENTIFIER '(' ')' { ERROR("Function declarator not supported"); } // ??? remove?
;
array_allocation
: IDENTIFIER '[' constant ']'
| array_allocation '[' constant ']'
;
parameter_type_list
: parameter_list ',' ELLIPSIS
| parameter_list
;
parameter_list
: parameter_declaration
| parameter_list ',' parameter_declaration
;
parameter_declaration
: declaration_specifiers declarator
| declaration_specifiers
;
initializer
: '{' initializer_list '}' { $$ = NULL; }
| '{' initializer_list ',' '}' { $$ = NULL; }
| assignment_expression { $$ = $1; }
;
initializer_list
: designation initializer
| initializer
| initializer_list ',' designation initializer
| initializer_list ',' initializer
;
designation
: designator_list '='
;
designator_list
: designator
| designator_list designator
;
designator
: '[' constant_expression ']'
| '.' IDENTIFIER
;
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
labeled_statement
: IDENTIFIER ':' statement
| CASE constant_expression ':' statement
| DEFAULT ':' statement
;
compound_statement
: '{' '}'
| '{' block_item_list '}'
;
block_item_list
: block_item
| block_item_list block_item
;
block_item
: declaration
| statement
;
expression_statement
: ';'
| expression ';'
;
selection_statement
: IF '(' expression ')' statement ELSE statement
| IF '(' expression ')' statement
| SYM_SWITCH '(' expression ')' statement
;
iteration_statement
: WHILE '(' expression ')' statement
| DO statement WHILE '(' expression ')' ';'
| FOR '(' expression_statement expression_statement ')' statement
| FOR '(' expression_statement expression_statement expression ')' statement
| FOR '(' declaration expression_statement ')' statement
| FOR '(' declaration expression_statement expression ')' statement
;
jump_statement
: GOTO IDENTIFIER ';'
| CONTINUE ';'
| BREAK ';'
| SYM_RETURN ';'
| SYM_RETURN expression ';'
;
toplevel
: translation_unit { jaf_toplevel = $1; }
;
translation_unit
: external_declaration { $$ = jaf_declarations(NULL, $1); }
| translation_unit external_declaration { $$ = jaf_declarations($1, $2); }
;
external_declaration
: function_definition { ERROR("Functions not yet supported"); }
| declaration { $$ = $1; }
;
function_definition
: declaration_specifiers declarator compound_statement
;
%%
#include <stdio.h>
void yyerror(const char *s)
{
fflush(stdout);
fprintf(stderr, "*** %s\n", s);
}
+181
View File
@@ -0,0 +1,181 @@
/* Copyright (C) 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://gnu.org/licenses/>.
*/
#include <stdlib.h>
#include "system4.h"
#include "jaf.h"
// TODO: better error messages
#define TYPE_ERROR(expr, expected) ERROR("Type error")
#define TYPE_CHECK(expr, expected) { if (expr->derived_type != expected) TYPE_ERROR(expr, expected); }
#define TYPE_CHECK_NUMERIC(expr) { if (expr->derived_type != JAF_INT && expr->derived_type != JAF_FLOAT) TYPE_ERROR(expr, JAF_INT); }
static void jaf_derive_types(struct jaf_expression *expr);
static enum jaf_type jaf_type_check_numeric(struct jaf_expression *expr)
{
if (expr->derived_type != JAF_INT && expr->derived_type != JAF_FLOAT)
TYPE_ERROR(expr, JAF_INT);
return expr->derived_type;
}
static enum jaf_type jaf_type_check_int(struct jaf_expression *expr)
{
if (expr->derived_type != JAF_INT)
TYPE_ERROR(expr, JAF_INT);
return expr->derived_type;
}
static enum jaf_type jaf_merge_types(enum jaf_type a, enum jaf_type b)
{
if (a == b)
return a;
if (a == JAF_INT && b == JAF_FLOAT)
return JAF_FLOAT;
if (a == JAF_FLOAT && b == JAF_INT)
return JAF_FLOAT;
ERROR("Incompatible types");
}
static enum jaf_type jaf_check_types_unary(struct jaf_expression *expr)
{
jaf_derive_types(expr->expr);
switch (expr->op) {
case JAF_UNARY_PLUS:
case JAF_UNARY_MINUS:
case JAF_PRE_INC:
case JAF_PRE_DEC:
case JAF_POST_INC:
case JAF_POST_DEC:
return jaf_type_check_numeric(expr->expr);
case JAF_BIT_NOT:
case JAF_LOG_NOT:
return jaf_type_check_int(expr->expr);
case JAF_AMPERSAND:
ERROR("Function types not supported");
default:
break;
}
ERROR("Unhandled unary operator");
}
static enum jaf_type jaf_check_types_binary(struct jaf_expression *expr)
{
jaf_derive_types(expr->lhs);
jaf_derive_types(expr->rhs);
switch (expr->op) {
// real ops
case JAF_MULTIPLY:
case JAF_DIVIDE:
case JAF_PLUS:
case JAF_MINUS:
return jaf_merge_types(jaf_type_check_numeric(expr->lhs), jaf_type_check_numeric(expr->rhs));
// integer ops
case JAF_REMAINDER:
case JAF_LSHIFT:
case JAF_RSHIFT:
case JAF_BIT_AND:
case JAF_BIT_XOR:
case JAF_BIT_IOR:
jaf_type_check_int(expr->lhs);
jaf_type_check_int(expr->rhs);
return JAF_INT;
// comparison ops
case JAF_LT:
case JAF_GT:
case JAF_LTE:
case JAF_GTE:
case JAF_EQ:
jaf_type_check_numeric(expr->lhs);
jaf_type_check_numeric(expr->rhs);
return JAF_INT;
// boolean ops
case JAF_LOG_AND:
case JAF_LOG_OR:
jaf_type_check_int(expr->lhs);
jaf_type_check_int(expr->rhs);
return JAF_INT;
case JAF_ASSIGN:
case JAF_MUL_ASSIGN:
case JAF_DIV_ASSIGN:
case JAF_MOD_ASSIGN:
case JAF_ADD_ASSIGN:
case JAF_SUB_ASSIGN:
case JAF_LSHIFT_ASSIGN:
case JAF_RSHIFT_ASSIGN:
case JAF_AND_ASSIGN:
case JAF_XOR_ASSIGN:
case JAF_OR_ASSIGN:
case JAF_REF_ASSIGN:
ERROR("Assignment expressions not supported");
default:
break;
}
ERROR("Unhandled binary operator");
}
static enum jaf_type jaf_check_types_ternary(struct jaf_expression *expr)
{
jaf_derive_types(expr->condition);
jaf_derive_types(expr->consequent);
jaf_derive_types(expr->alternative);
TYPE_CHECK(expr->condition, JAF_INT);
if (expr->consequent->derived_type != expr->alternative->derived_type) {
// TODO: better error message
ERROR("Mismatched types in conditional expression");
}
return expr->consequent->derived_type;
}
static enum jaf_type _jaf_check_types(struct jaf_expression *expr)
{
switch (expr->type) {
case JAF_EXP_INT:
return JAF_INT;
case JAF_EXP_FLOAT:
return JAF_FLOAT;
case JAF_EXP_STRING:
return JAF_STRING;
case JAF_EXP_IDENTIFIER:
// TODO: need environment
ERROR("Identifiers not supported");
case JAF_EXP_UNARY:
return jaf_check_types_unary(expr);
case JAF_EXP_BINARY:
return jaf_check_types_binary(expr);
case JAF_EXP_TERNARY:
return jaf_check_types_ternary(expr);
default:
break;
}
ERROR("Unhandled expression type");
}
static void jaf_derive_types(struct jaf_expression *expr)
{
expr->derived_type = _jaf_check_types(expr);
}
void jaf_check_types(struct jaf_expression *expr, struct jaf_type_specifier *type)
{
jaf_derive_types(expr);
// TODO: struct types, etc.
if (expr->derived_type != type->type) {
TYPE_ERROR(expr, type->type);
}
}
+6 -2
View File
@@ -3,14 +3,18 @@ ainedit = ['../../cJSON.c',
'../conv.c',
'ainedit.c',
'asm.c',
'jaf_ast.c',
'jaf_compile.c',
'jaf_eval.c',
'jaf_types.c',
'json.c',
'repack.c',
'text.c',
'transcode.c',
]
ainedit += flexgen.process('asm_lexer.l', 'text_lexer.l')
ainedit += bisongen.process('asm_parser.y', 'text_parser.y')
ainedit += flexgen.process('asm_lexer.l', 'text_lexer.l', 'jaf_lexer.l')
ainedit += bisongen.process('asm_parser.y', 'text_parser.y', 'jaf_parser.y')
executable('ainedit', ainedit,
dependencies : tool_deps,