Forked from
Michal Vlasák / ni-run-template
10 commits behind, 68 commits ahead of the upstream repository.
-
Michal Štěpánek authoredMichal Štěpánek authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
parser.h 4.88 KiB
// FML parser
// Michal Vlasák, FIT CTU, 2023
#pragma once
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdalign.h>
#include <string.h>
#include <time.h>
#include "arena.h"
// See the bottom of this file for parser API.
// This header mainly defines the AST emitted by the reference FML parser and
// a few utility types.
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
// `Str` is a pair of pointer and length and represents an immutable string. It
// can be constructed from C string literals with the below `STR` macro.
typedef struct {
const u8 *str;
size_t len;
} Str;
#define STR(lit) (Str) { .str = (const u8 *) lit, .len = sizeof(lit) - 1 }
// Compare equality of strings, returns true if equal, false otherwise.
bool str_eq(Str a, Str b);
// Lexicographically compare two strings. Returns:
// - 0 if the strings are equal,
// - a negative number if the first string is less than the second
// - a positive number if the first string is greater than the second
int str_cmp(Str a, Str b);
// Get current time and save it into `ts`. Platform details may differ, but
// `struct timespec` should be at least similar to the following:
//
// struct timespec {
// long long tv_sec; /* seconds */
// long tv_nsec; /* nanoseconds */
// }
//
// An example of use:
//
// struct timespec ts;
// if (!time_get(&ts)) {
// exit(1);
// }
// printf("%lld%09ld\n", ts.tv_sec, ts.tv_nsec);
//
bool time_get(struct timespec *ts);
// See https://courses.fit.cvut.cz/NI-RUN/specs/ast.html for details about the
// AST.
typedef enum {
AST_NULL,
AST_BOOLEAN,
AST_INTEGER,
AST_ARRAY,
AST_OBJECT,
AST_FUNCTION,
AST_DEFINITION,
AST_VARIABLE_ACCESS,
AST_VARIABLE_ASSIGNMENT,
AST_INDEX_ACCESS,
AST_INDEX_ASSIGNMENT,
AST_FIELD_ACCESS,
AST_FIELD_ASSIGNMENT,
AST_FUNCTION_CALL,
AST_METHOD_CALL,
AST_CONDITIONAL,
AST_LOOP,
AST_PRINT,
AST_BLOCK,
AST_TOP,
} AstKind;
typedef struct {
AstKind kind;
} Ast;
typedef struct {
Ast base;
} AstNull;
typedef struct {
Ast base;
bool value;
} AstBoolean;
typedef struct {
Ast base;
i32 value;
} AstInteger;
typedef struct {
Ast base;
Ast *size;
Ast *initializer;
} AstArray;
typedef struct {
Ast base;
Ast *extends;
Ast **members;
size_t member_cnt;
} AstObject;
typedef struct {
Ast base;
Str *parameters;
size_t parameter_cnt;
Ast *body;
} AstFunction;
typedef struct {
Ast base;
Str name;
Ast *value;
} AstDefinition;
typedef struct {
Ast base;
Str name;
} AstVariableAccess;
typedef struct {
Ast base;
Str name;
Ast *value;
} AstVariableAssignment;
typedef struct {
Ast base;
Ast *object;
Ast *index;
} AstIndexAccess;
typedef struct {
Ast base;
Ast *object;
Ast *index;
Ast *value;
} AstIndexAssignment;
typedef struct {
Ast base;
Ast *object;
Str field;
} AstFieldAccess;
typedef struct {
Ast base;
Ast *object;
Str field;
Ast *value;
} AstFieldAssignment;
typedef struct {
Ast base;
Ast *function;
Ast **arguments;
size_t argument_cnt;
} AstFunctionCall;
typedef struct {
Ast base;
Ast* object;
Str name;
Ast **arguments;
size_t argument_cnt;
} AstMethodCall;
typedef struct {
Ast base;
Ast *condition;
Ast *consequent;
Ast *alternative;
} AstConditional;
typedef struct {
Ast base;
Ast *condition;
Ast *body;
} AstLoop;
typedef struct {
Ast base;
Str format;
Ast **arguments;
size_t argument_cnt;
} AstPrint;
typedef struct {
Ast base;
Ast **expressions;
size_t expression_cnt;
} AstBlock;
typedef struct {
Ast base;
Ast **expressions;
size_t expression_cnt;
} AstTop;
// Parser takes a source code (`Str`) as input and produces AST (`Ast`) as an
// output. The function `parse` is the most general way of using the parser. It
// receives an `Arena` in which the `Ast` will be allocated (the user is
// responsible for deallocation) and `GArena` which will be used as scratch
// space (parser may allocate in it, but returns it to the previous state).
//
// In case of any encountered errors, `NULL` is ultimately returned, errors are
// reported to the caller with a callback function and the space allocated in
// the `Arena` by the parser is deallocated (restored to the state at the start
// of parsing).
//
// For simplicity, the `parse_src` function can be used instead. It uses the
// predefined example `parser_error_cb` callback, which is defined to report
// errors to standard error (see it also for details about the callback
// behavior).
Ast *parse_src(Arena *arena, Str source);
Ast *parse(
Arena *arena,
GArena *scratch,
Str source,
void (*error_callback)(
void *user_data,
const u8 *err_pos,
const char *msg,
va_list ap
),
void *user_data
);
void parser_error_cb(
void *user_data,
const u8 *err_pos,
const char *msg,
va_list ap
);