Merge branch 'future' of github.com:justinmeza/lci into future

This commit is contained in:
Justin J. Meza 2013-03-18 13:17:35 -04:00
commit da61b7bcee
33 changed files with 696 additions and 90 deletions

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
CMakeCache.txt
CMakeFiles
Makefile
cmake_install.cmake
install_manifest.txt
CTestTestfile.cmake
tags

View File

@ -28,6 +28,7 @@ SET(HDRS
tokenizer.h
unicode.h
error.h
binding.h
)
SET(SRCS
@ -38,10 +39,11 @@ SET(SRCS
tokenizer.c
unicode.c
error.c
binding.c
)
add_executable(lci ${SRCS} ${HDRS})
target_link_libraries(lci m)
target_link_libraries(lci m ncurses readline)
add_subdirectory(test)
install(
TARGETS lci

195
binding.c Normal file
View File

@ -0,0 +1,195 @@
#include "binding.h"
struct returnobject;
struct scopeobject;
ValueObject *getArg(struct scopeobject *scope, char *name)
{
IdentifierNode *id = createIdentifierNode(IT_DIRECT, (void *)copyString(name), NULL, NULL, 0);
ValueObject *val = getScopeValueLocal(scope, scope, id);
deleteIdentifierNode(id);
return val;
}
struct returnobject *fopenWrapper(struct scopeobject *scope)
{
ValueObject *arg1 = getArg(scope, "filename");
ValueObject *arg2 = getArg(scope, "mode");
char *filename = getString(arg1);
char *mode = getString(arg2);
FILE *f = fopen(filename, mode);
ValueObject *ret = createBlobValueObject(f);
return createReturnObject(RT_RETURN, ret);
}
struct returnobject *freadWrapper(struct scopeobject *scope)
{
ValueObject *arg1 = getArg(scope, "file");
ValueObject *arg2 = getArg(scope, "length");
FILE *file = (FILE *)getBlob(arg1);
int length = getInteger(arg2);
char *buf = malloc((sizeof(char) * length) + 1);
fread(buf, 1, length, file);
buf[length] = '\0';
ValueObject *ret = createStringValueObject(buf);
return createReturnObject(RT_RETURN, ret);
}
struct returnobject *fwriteWrapper(struct scopeobject *scope)
{
ValueObject *arg1 = getArg(scope, "file");
ValueObject *arg2 = getArg(scope, "data");
FILE *file = (FILE *)getBlob(arg1);
char *data = getString(arg2);
fwrite(data, 1, strlen(data), file);
return createReturnObject(RT_DEFAULT, NULL);
}
struct returnobject *fcloseWrapper(struct scopeobject *scope)
{
ValueObject *arg1 = getArg(scope, "file");
FILE *file = (FILE *)getBlob(arg1);
fclose(file);
deleteValueObject(arg1);
return createReturnObject(RT_DEFAULT, NULL);
}
void loadLibrary(ScopeObject *scope, IdentifierNode *target)
{
char *name = NULL;
int status;
ScopeObject *lib = NULL;
IdentifierNode *id = NULL;
ValueObject *val = NULL;
if (target == NULL) return;
name = resolveIdentifierName(target, scope);
if (!name) goto loadLibraryAbort;
if (!strcmp(name, "STDIO")) {
lib = createScopeObject(scope);
if (!lib) goto loadLibraryAbort;
loadBinding(lib, "FOPENIN", "filename mode", &fopenWrapper);
loadBinding(lib, "FREADIN", "file length", &freadWrapper);
loadBinding(lib, "FWRITIN", "file data", &fwriteWrapper);
loadBinding(lib, "FCLOSIN", "file", &fcloseWrapper);
id = createIdentifierNode(IT_DIRECT, (void *)copyString("STDIO"), NULL, NULL, 0);
if (!id) goto loadLibraryAbort;
if (!createScopeValue(scope, scope, id)) goto loadLibraryAbort;
val = createArrayValueObject(lib);
if (!val) goto loadLibraryAbort;
lib = NULL;
if (!updateScopeValue(scope, scope, id, val)) goto loadLibraryAbort;
}
if (name) free(name);
return;
loadLibraryAbort: /* In case something goes wrong... */
/* Clean up any allocated structures */
if (name) free(name);
if (lib) deleteScopeObject(lib);
if (id) deleteIdentifierNode(id);
if (val) deleteValueObject(val);
return;
}
void loadBinding(ScopeObject *scope, char *name, const char *args, struct returnobject *(*binding)(struct scopeobject *))
{
IdentifierNode *id = NULL;
StmtNodeList *stmts = NULL;
BindingStmtNode *stmt = NULL;
StmtNode *wrapper = NULL;
int status;
BlockNode *body = NULL;
IdentifierNodeList *ids = NULL;
IdentifierNode *arg = NULL;
if (name == NULL || binding == NULL) return;
id = createIdentifierNode(IT_DIRECT, (void *)copyString(name), NULL, NULL, 0);
if (!id) goto loadBindingAbort;
stmts = createStmtNodeList();
if (!stmts) goto loadBindingAbort;
stmt = createBindingStmtNode(binding);
if (!stmt) goto loadBindingAbort;
wrapper = createStmtNode(ST_BINDING, stmt);
if (!wrapper) goto loadBindingAbort;
stmt = NULL;
status = addStmtNode(stmts, wrapper);
if (!status) goto loadBindingAbort;
wrapper = NULL;
body = createBlockNode(stmts);
if (!body) goto loadBindingAbort;
stmts = NULL;
ids = createIdentifierNodeList();
if (!ids) goto loadBindingAbort;
const char *start = args;
while (start != NULL) {
char *end = strchr(start, ' ');
char *temp = NULL;
unsigned int len = 0;
if (end != NULL) len = (end - start);
else len = strlen(start);
temp = malloc(sizeof(char) * (len + 1));
strncpy(temp, start, len);
temp[len] = '\0';
if (end != NULL) start = (end + 1);
else start = NULL;
arg = createIdentifierNode(IT_DIRECT, (void *)temp, NULL, NULL, 0);
if (!arg) goto loadBindingAbort;
status = addIdentifierNode(ids, arg);
if (!status) goto loadBindingAbort;
}
FuncDefStmtNode *interface = createFuncDefStmtNode(NULL, id, ids, body);
if (!interface) goto loadBindingAbort;
ValueObject *val = createFunctionValueObject(interface);
if (!val) goto loadBindingAbort;
createScopeValue(scope, scope, id);
updateScopeValue(scope, scope, id, val);
return;
loadBindingAbort: /* In case something goes wrong... */
if (id) deleteIdentifierNode(id);
if (val) deleteValueObject(val);
else if (interface) deleteFuncDefStmtNode(interface);
else {
if (arg) deleteIdentifierNode(arg);
if (ids) deleteIdentifierNodeList(ids);
if (body) deleteBlockNode(body);
if (stmts) deleteStmtNodeList(stmts);
if (wrapper) deleteStmtNode(wrapper);
if (stmt) deleteBindingStmtNode(stmt);
}
return;
}

30
binding.h Normal file
View File

@ -0,0 +1,30 @@
/**
* Structures and functions for binding to external libraries.
*
* \file binding.h
*
* \author Justin J. Meza
*
* \date 2013
*/
#ifndef __BINDING_H__
#define __BINDING_H__
#include <stdio.h>
#include "interpreter.h"
/**
* Stores a binding to an external library call to export.
*/
typedef struct {
IdentifierNode *library; /**< The library this binding belongs to. */
FuncDefStmtNode *interface; /**< The interface that exports the binding. */
} Binding;
ValueObject *getArg(struct scopeobject *, char *);
void loadLibrary(ScopeObject *, IdentifierNode *);
void loadBinding(ScopeObject *, char *, const char *, struct returnobject *(*)(struct scopeobject *));
#endif /* __BINDING_H__ */

View File

@ -226,12 +226,12 @@ static const int err_codes[] = {
538, /* IN_FUNCTION_NAME_USED_BY_VARIABLE */
};
void error(ErrorType e, ...)
int error(ErrorType e, ...)
{
va_list args;
va_start(args, e);
vfprintf(stderr, err_msgs[e], args);
va_end(args);
exit(err_codes[e]);
return err_codes[e];
}

View File

@ -104,6 +104,6 @@ typedef enum {
IN_FUNCTION_NAME_USED_BY_VARIABLE,
} ErrorType;
void error(ErrorType, ...);
int error(ErrorType, ...);
#endif /* __ERROR_H__ */

View File

@ -16,7 +16,7 @@ def positiveInt(string):
Runs a subprocess using the command parameter.
Before running the command it displays a message
that contains the provided description and where
the output will be sent. If an error occurs,
the output will be sent. If an error occurs,
the errorMsg is displayed.
"""
def runSubProc(command, description, errorMsg, output):
@ -28,7 +28,7 @@ def runSubProc(command, description, errorMsg, output):
if os.name == "nt":
proc = subprocess.Popen(command, stdout=outputFile, stderr=subprocess.STDOUT, shell=True)
else:
proc = subprocess.Popen(command, stdout=outputFile, stderr=subprocess.STDOUT)
proc = subprocess.Popen(command, stdout=outputFile, stderr=subprocess.STDOUT, shell=True)
proc.wait()
if proc.returncode != 0:
print("Error installing: " + errorMsg)
@ -72,7 +72,7 @@ runSubProc(
"configure.out")
runSubProc(
[makeCommand, "-j"+j],
[makeCommand, "-j"+j],
"Running make ",
"There was a make error",
"make.out")
@ -80,20 +80,20 @@ runSubProc(
if args.buildDocs:
runSubProc(
[makeCommand, "-j"+j, "docs"],
[makeCommand, "-j"+j, "docs"],
"Building documentation ",
"There was a documentation building error",
"docs.out")
runSubProc(
[makeCommand, "install"],
makeCommand + " install",
"Installing ",
"There was an installation error",
"install.out")
if args.runTests:
runSubProc(
["ctest", "-j"+j],
["ctest", "-j"+j],
"Testing ",
"There was a testing error",
"test.out")

View File

@ -306,6 +306,30 @@ ValueObject *createArrayValueObject(ScopeObject *parent)
return p;
}
/**
* Creates a blob-type value.
*
* \param [in] data The binary blob data to store.
*
* \note \a data is stored as-is; no copy of it is made.
*
* \return A string-type value equalling \a data.
*
* \retval NULL Memory allocation failed.
*/
ValueObject *createBlobValueObject(void *data)
{
ValueObject *p = malloc(sizeof(ValueObject));
if (!p) {
perror("malloc");
return NULL;
}
p->type = VT_BLOB;
p->data.b = data;
p->semaphore = 1;
return p;
}
/**
* Copies a value.
*
@ -3589,7 +3613,7 @@ ReturnObject *interpretExprStmtNode(StmtNode *node,
*
* \param [in] scope The scope to evaluate \a node under.
*
* \pre \a node contains a statement created by createAltArrayDefNode().
* \pre \a node contains a statement created by createAltArrayDefStmtNode().
*
* \return A pointer to a default return value.
*
@ -3639,11 +3663,52 @@ ReturnObject *interpretAltArrayDefStmtNode(StmtNode *node,
return createReturnObject(RT_DEFAULT, NULL);
}
/**
* Interprets a binding statement.
*
* \param [in] node The statement to interpret.
*
* \param [in] scope The scope to evaluate \a node under.
*
* \pre \a node contains a statement created by createBindingStmtNode().
*
* \return A pointer to a default return value.
*
* \retval NULL An error occurred during interpretation.
*/
ReturnObject *interpretBindingStmtNode(StmtNode *node,
ScopeObject *scope)
{
BindingStmtNode *stmt = (BindingStmtNode *)node->stmt;
return (stmt->binding)(scope);
}
/**
* Interprets a library import statement.
*
* \param [in] node The statement to interpret.
*
* \param [in] scope The scope to evaluate \a node under.
*
* \pre \a node contains a statement created by createImportStmtNode().
*
* \return A pointer to a default return value.
*
* \retval NULL An error occurred during interpretation.
*/
ReturnObject *interpretImportStmtNode(StmtNode *node,
ScopeObject *scope)
{
ImportStmtNode *stmt = (ImportStmtNode *)node->stmt;
loadLibrary(scope, stmt->name);
return createReturnObject(RT_DEFAULT, NULL);
}
/*
* A jump table for statements. The index of a function in the table is given
* by its its index in the enumerated StmtType type.
*/
static ReturnObject *(*StmtJumpTable[14])(StmtNode *, ScopeObject *) = {
static ReturnObject *(*StmtJumpTable[16])(StmtNode *, ScopeObject *) = {
interpretCastStmtNode,
interpretPrintStmtNode,
interpretInputStmtNode,
@ -3657,7 +3722,9 @@ static ReturnObject *(*StmtJumpTable[14])(StmtNode *, ScopeObject *) = {
interpretDeallocationStmtNode,
interpretFuncDefStmtNode,
interpretExprStmtNode,
interpretAltArrayDefStmtNode };
interpretAltArrayDefStmtNode,
interpretBindingStmtNode,
interpretImportStmtNode };
/**
* Interprets a statement.
@ -3741,6 +3808,8 @@ ReturnObject *interpretBlockNode(BlockNode *node,
*
* \param [in] main The main block of code to interpret.
*
* \param [in] scope The scope to evaluate \a main under.
*
* \pre \a main contains a block of code created by parseMainNode().
*
* \return The final status of the program.
@ -3749,12 +3818,12 @@ ReturnObject *interpretBlockNode(BlockNode *node,
*
* \retval 1 An error occurred while interpreting \a main.
*/
int interpretMainNode(MainNode *main)
int interpretMainNodeScope(MainNode *main, ScopeObject *scope)
{
ReturnObject *ret = NULL;
if (!main) return 1;
ret = interpretBlockNode(main->block, NULL);
if (!ret) return 1;
ret = interpretBlockNode(main->block, scope);
if (!ret) return 1;
deleteReturnObject(ret);
return 0;
}

View File

@ -46,6 +46,11 @@
*/
#define getArray(value) (value->data.a)
/**
* Retrieves a value's blob data.
*/
#define getBlob(value) (value->data.b)
/**
* Represents a value type.
*/
@ -56,7 +61,8 @@ typedef enum {
VT_STRING, /**< A string value. */
VT_NIL, /**< Represents no value. */
VT_FUNC, /**< A function. */
VT_ARRAY /**< An array. */
VT_ARRAY, /**< An array. */
VT_BLOB /**< A binary blob of data. */
} ValueType;
/**
@ -68,6 +74,7 @@ typedef union {
char *s; /**< String data. */
FuncDefStmtNode *fn; /**< Function data. */
struct scopeobject *a; /**< Array data. */
void *b; /**< Binary blob data. */
} ValueData;
/**
@ -101,7 +108,7 @@ typedef enum {
/**
* Stores return state.
*/
typedef struct {
typedef struct returnobject {
ReturnType type; /**< The type of return encountered. */
ValueObject *value; /**< The optional return value. */
} ReturnObject;
@ -144,6 +151,7 @@ ValueObject *createFloatValueObject(float);
ValueObject *createStringValueObject(char *);
ValueObject *createFunctionValueObject(FuncDefStmtNode *);
ValueObject *createArrayValueObject(ScopeObject *);
ValueObject *createBlobValueObject(void *);
ValueObject *copyValueObject(ValueObject *);
void deleteValueObject(ValueObject *);
/**@}*/
@ -252,6 +260,8 @@ ReturnObject *interpretDeallocationStmtNode(StmtNode *, ScopeObject *);
ReturnObject *interpretFuncDefStmtNode(StmtNode *, ScopeObject *);
ReturnObject *interpretExprStmtNode(StmtNode *, ScopeObject *);
ReturnObject *interpretAltArrayDefStmtNode(StmtNode *, ScopeObject *);
ReturnObject *interpretBindingStmtNode(StmtNode *, ScopeObject *);
ReturnObject *interpretImportStmtNode(StmtNode *, ScopeObject *);
/**@}*/
/**

17
lexer.c
View File

@ -198,6 +198,21 @@ LexemeList *scanBuffer(const char *buffer, unsigned int size, const char *fname)
start += 2;
continue;
}
/* Question mark (?) is its own lexeme */
if (*start == '?') {
Lexeme *lex = createLexeme("?", fname, line);
if (!lex) {
deleteLexemeList(list);
return NULL;
}
if (!addLexeme(list, lex)) {
deleteLexeme(lex);
deleteLexemeList(list);
return NULL;
}
start++;
continue;
}
/* Skip over leading whitespace */
while (isspace(*start)) {
unsigned int newline = 0;
@ -292,6 +307,7 @@ LexemeList *scanBuffer(const char *buffer, unsigned int size, const char *fname)
if (start[len] && !isspace(start[len])
&& *(start + len) != ','
&& *(start + len) != '!'
&& *(start + len) != '?'
&& strncmp(start + len, "'Z", 2)
&& strncmp(start + len, "...", 3)
&& strncmp(start + len, "\xE2\x80\xA6", 3)) {
@ -305,6 +321,7 @@ LexemeList *scanBuffer(const char *buffer, unsigned int size, const char *fname)
while (start[len] && !isspace(start[len])
&& *(start + len) != ','
&& *(start + len) != '!'
&& *(start + len) != '?'
&& strncmp(start + len, "'Z", 2)
&& strncmp(start + len, "...", 3)
&& strncmp(start + len, "\xE2\x80\xA6", 3))

105
main.c
View File

@ -109,6 +109,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "lexer.h"
#include "tokenizer.h"
@ -120,10 +122,11 @@
static char *program_name;
static char *shortopt = "hv";
static char *shortopt = "hvi";
static struct option longopt[] = {
{ "help", no_argument, NULL, (int)'h' },
{ "version", no_argument, NULL, (int)'v' },
{ "interactive", no_argument, NULL, (int)'i' },
{ 0, 0, 0, 0 }
};
@ -132,26 +135,52 @@ static void help(void) {
Usage: %s [FILE] ... \n\
Interpret FILE(s) as LOLCODE. Let FILE be '-' for stdin.\n\
-h, --help\t\toutput this help\n\
-v, --version\t\tprogram version\n", program_name);
-v, --version\t\tprogram version\n\
-i, --interactive\tinteractive prompt\n", program_name);
}
static void version (char *revision) {
fprintf(stderr, "%s %s\n", program_name, revision);
}
int pipeline(char *buffer, unsigned int length, const char *fname, ScopeObject *scope)
{
LexemeList *lexemes = NULL;
Token **tokens = NULL;
MainNode *node = NULL;
if (!(lexemes = scanBuffer(buffer, length, fname))) {
free(buffer);
return 1;
}
free(buffer);
if (!(tokens = tokenizeLexemes(lexemes))) {
deleteLexemeList(lexemes);
return 1;
}
deleteLexemeList(lexemes);
if (!(node = parseMainNode(tokens))) {
deleteTokens(tokens);
return 1;
}
deleteTokens(tokens);
if (interpretMainNodeScope(node, NULL)) {
deleteMainNode(node);
return 1;
}
deleteMainNode(node);
return 0;
}
int main(int argc, char **argv)
{
unsigned int size = 0;
unsigned int length = 0;
char *buffer = NULL;
LexemeList *lexemes = NULL;
Token **tokens = NULL;
MainNode *node = NULL;
char *fname = NULL;
FILE *file = NULL;
int ch;
char *revision = "v0.10.3";
char *revision = "v0.11.4";
program_name = argv[0];
while ((ch = getopt_long(argc, argv, shortopt, longopt, NULL)) != -1) {
@ -165,15 +194,50 @@ int main(int argc, char **argv)
case 'v':
version(revision);
exit(EXIT_SUCCESS);
case 'i':
{
char *line = NULL;
/* Save state between lines using a ScopeObject structure */
ScopeObject *scope = createScopeObject(NULL);
if (!scope) return 1;
while (line = readline("lci> ")) {
char *pre = "HAI 1.4\n";
char *post = "\n\nKTHXBYE\n";
char *code = NULL;
size = strlen(line);
buffer = realloc(buffer, sizeof(char) * (length + size + 1));
strncpy(buffer + length, line, size);
buffer[length + size] = '\n';
length += size + 1;
add_history(line);
/* Intercept KTHXBYE to quit */
if (!strcmp(line, "KTHXBYE")) {
break;
}
/* Intercept HALP to display help message */
else if (!strcmp(line, "HALP")) {
version(revision);
help();
continue;
}
/* Create staged code file */
code = malloc(sizeof(char) * (strlen(pre) + size + strlen(post) + 1));
strcpy(code, pre);
strncpy(code + strlen(pre), line, size);
strcpy(code + strlen(pre) + size, post);
code[strlen(pre) + size + strlen(post)] = '\0';
pipeline(code, strlen(code), "interactive", scope);
}
free(buffer);
deleteScopeObject(scope);
exit(EXIT_SUCCESS);
}
}
}
for (; optind < argc; optind++) {
size = length = 0;
buffer = fname = NULL;
lexemes = NULL;
tokens = NULL;
node = NULL;
file = NULL;
if (!strncmp(argv[optind],"-\0",2)) {
@ -228,28 +292,7 @@ int main(int argc, char **argv)
printf("%c%c%c", 0xef, 0xbb, 0xbf);
}
/* Begin main pipeline */
if (!(lexemes = scanBuffer(buffer, length, fname))) {
free(buffer);
return 1;
}
free(buffer);
if (!(tokens = tokenizeLexemes(lexemes))) {
deleteLexemeList(lexemes);
return 1;
}
deleteLexemeList(lexemes);
if (!(node = parseMainNode(tokens))) {
deleteTokens(tokens);
return 1;
}
deleteTokens(tokens);
if (interpretMainNode(node)) {
deleteMainNode(node);
return 1;
}
deleteMainNode(node);
/* End main pipeline */
return pipeline(buffer, length, fname, NULL);
}

165
parser.c
View File

@ -514,6 +514,16 @@ void deleteStmtNode(StmtNode *node)
deleteAltArrayDefStmtNode(stmt);
break;
}
case ST_BINDING: {
BindingStmtNode *stmt = (BindingStmtNode *)node->stmt;
deleteBindingStmtNode(stmt);
break;
}
case ST_IMPORT: {
ImportStmtNode *stmt = (ImportStmtNode *)node->stmt;
deleteImportStmtNode(stmt);
break;
}
default:
error(PR_UNKNOWN_STATEMENT_TYPE);
break;
@ -1105,6 +1115,73 @@ void deleteAltArrayDefStmtNode(AltArrayDefStmtNode *node)
free(node);
}
/**
* Creates a library import statement.
*
* \param [in] name The name of the library to import.
*
* \return A pointer to a library import statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
ImportStmtNode *createImportStmtNode(IdentifierNode *name)
{
ImportStmtNode *p = malloc(sizeof(ImportStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->name = name;
return p;
}
/**
* Deletes a library import statement.
*
* \param [in,out] node The library import statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteImportStmtNode(ImportStmtNode *node)
{
if (!node) return;
deleteIdentifierNode(node->name);
free(node);
}
/**
* Creates a binding statement.
*
* \param [in] binding A pointer to the function that defines the binding.
*
* \return A pointer to a binding statement with the desired properties.
*
* \retval NULL Memory allocation failed.
*/
BindingStmtNode *createBindingStmtNode(struct returnobject *(*binding)(struct scopeobject *))
{
BindingStmtNode *p = malloc(sizeof(BindingStmtNode));
if (!p) {
perror("malloc");
return NULL;
}
p->binding = binding;
return p;
}
/**
* Deletes a binding statement.
*
* \param [in,out] node The binding statement to delete.
*
* \post The memory at \a node and all of its members will be freed.
*/
void deleteBindingStmtNode(BindingStmtNode *node)
{
if (!node) return;
free(node);
}
/**
* Creates an expression.
*
@ -2820,6 +2897,13 @@ StmtNode *parseIfThenElseStmtNode(Token ***tokenp)
goto parseIfThenElseStmtNodeAbort;
}
/* Remove the question mark from the token stream */
status = acceptToken(&tokens, TT_QUESTION);
if (!status) {
parser_error_expected_token(TT_QUESTION, tokens);
goto parseIfThenElseStmtNodeAbort;
}
/* The if keyword must appear on its own line */
if (!acceptToken(&tokens, TT_NEWLINE)) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
@ -2973,6 +3057,13 @@ StmtNode *parseSwitchStmtNode(Token ***tokenp)
goto parseSwitchStmtNodeAbort;
}
/* Remove the question mark from the token stream */
status = acceptToken(&tokens, TT_QUESTION);
if (!status) {
parser_error_expected_token(TT_QUESTION, tokens);
goto parseSwitchStmtNodeAbort;
}
/* The switch keyword must appear on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
@ -3872,6 +3963,76 @@ parseAltArrayDefStmtNodeAbort: /* Exception handling */
return NULL;
}
/**
* Parses tokens into a library import statement.
*
* \param [in] tokenp The position in a token list to start parsing at.
*
* \post \a tokenp will point to the next unparsed token.
*
* \return A pointer to a return statement.
*
* \retval NULL Unable to parse.
*/
StmtNode *parseImportStmtNode(Token ***tokenp)
{
IdentifierNode *value = NULL;
ImportStmtNode *stmt = NULL;
StmtNode *ret = NULL;
int status;
/* Work from a copy of the token stream in case something goes wrong */
Token **tokens = *tokenp;
#ifdef DEBUG
debug("ST_IMPORT");
#endif
/* Remove the library import keyword from the token stream */
status = acceptToken(&tokens, TT_CANHAS);
if (!status) {
parser_error_expected_token(TT_CANHAS, tokens);
goto parseImportStmtNodeAbort;
}
/* Parse the library name */
value = parseIdentifierNode(&tokens);
if (!value) goto parseImportStmtNodeAbort;
/* Check for the question mark token (currently optional) */
acceptToken(&tokens, TT_QUESTION);
/* The library import statement must reside on its own line */
status = acceptToken(&tokens, TT_NEWLINE);
if (!status) {
parser_error(PR_EXPECTED_END_OF_EXPRESSION, tokens);
goto parseImportStmtNodeAbort;
}
/* Create the new ImportStmtNode structure */
stmt = createImportStmtNode(value);
if (!stmt) goto parseImportStmtNodeAbort;
/* Create the new StmtNode structure */
ret = createStmtNode(ST_IMPORT, stmt);
if (!ret) goto parseImportStmtNodeAbort;
/* Since we're successful, update the token stream */
*tokenp = tokens;
return ret;
parseImportStmtNodeAbort: /* Exception handling */
/* Clean up any allocated structures */
if (ret) deleteStmtNode(ret);
else if (stmt) deleteImportStmtNode(stmt);
else {
if (value) deleteIdentifierNode(value);
}
return NULL;
}
/**
* Parses tokens into a statement.
*
@ -3990,6 +4151,10 @@ StmtNode *parseStmtNode(Token ***tokenp)
else if (peekToken(&tokens, TT_OHAIIM)) {
ret = parseAltArrayDefStmtNode(tokenp);
}
/* Library import statement */
else if (peekToken(&tokens, TT_CANHAS)) {
ret = parseImportStmtNode(tokenp);
}
/* Bare expression */
else if ((expr = parseExprNode(&tokens))) {
int status;

View File

@ -228,6 +228,8 @@ typedef enum {
ST_FUNCDEF, /**< Function definition statement. */
ST_EXPR, /**< Expression statement. */
ST_ALTARRAYDEF, /**< Function definition statement. */
ST_BINDING, /**< Binding to external library. */
ST_IMPORT, /**< Library import statement. */
} StmtType;
/**
@ -364,6 +366,22 @@ typedef struct {
IdentifierNode *parent; /**< An optional inherited array. */
} AltArrayDefStmtNode;
/**
* Stores a library import statement.
*/
typedef struct {
IdentifierNode *name; /**< The name of the library to import. */
} ImportStmtNode;
/**
* Stores a binding to a native function.
*/
struct returnobject;
struct scopeobject;
typedef struct {
struct returnobject *(*binding)(struct scopeobject *); /**< The function that implements the binding. */
} BindingStmtNode;
/**
* Stores the main code block of a program.
*
@ -716,6 +734,28 @@ AltArrayDefStmtNode *createAltArrayDefStmtNode(IdentifierNode *, BlockNode *, Id
void deleteAltArrayDefStmtNode(AltArrayDefStmtNode *);
/**@}*/
/**
* \name ImportStmtNode modifiers
*
* Functions for creating and deleting ImportStmtNodes.
*/
/**@{*/
ImportStmtNode *createImportStmtNode(IdentifierNode *);
void deleteImportStmtNode(ImportStmtNode *);
/**@}*/
/**
* \name BindingStmtNode modifiers
*
* Functions for creating and deleting BindingStmtNodes.
*/
/**@{*/
struct returnobject;
struct scopeobject;
BindingStmtNode *createBindingStmtNode(struct returnobject *(*)(struct scopeobject *));
void deleteBindingStmtNode(BindingStmtNode *);
/**@}*/
/**
* \name ExprNode modifiers
*
@ -801,6 +841,7 @@ StmtNode *parseLoopStmtNode(Token ***);
StmtNode *parseDeallocationStmtNode(Token ***);
StmtNode *parseFuncDefStmtNode(Token ***);
StmtNode *parseAltArrayDefStmtNode(Token ***);
StmtNode *parseImportStmtNode(Token ***);
/**@}*/
/**

View File

@ -1,12 +1,4 @@
add_subdirectory(1-EmptyMainBlock)
add_subdirectory(10-CommasSeparate)
add_subdirectory(11-EllipsesJoinLF)
add_subdirectory(12-EllipsesJoinCR)
add_subdirectory(13-EllipsesJoinCRLF)
add_subdirectory(14-NoNewlineAfterJoinLF)
add_subdirectory(15-NoNewlineAfterJoinCR)
add_subdirectory(16-NoNewlineAfterJoinCRLF)
add_subdirectory(17-Includes)
add_subdirectory(2-MustBeginWithHAI)
add_subdirectory(3-MustIncludeVersion)
add_subdirectory(4-MustEndWithKTHXBYE)
@ -15,3 +7,11 @@ add_subdirectory(6-WhitespaceBetweenTokens)
add_subdirectory(7-NewlineLF)
add_subdirectory(8-NewlineCR)
add_subdirectory(9-NewlineCRLF)
add_subdirectory(10-CommasSeparate)
add_subdirectory(11-EllipsesJoinLF)
add_subdirectory(12-EllipsesJoinCR)
add_subdirectory(13-EllipsesJoinCRLF)
add_subdirectory(14-NoNewlineAfterJoinLF)
add_subdirectory(15-NoNewlineAfterJoinCR)
add_subdirectory(16-NoNewlineAfterJoinCRLF)
add_subdirectory(17-Includes)

View File

@ -1,7 +1,4 @@
add_subdirectory(1-EmptyArray)
add_subdirectory(10-CallingObjectInitialization)
add_subdirectory(11-AlternateSyntax)
add_subdirectory(12-Inheritance)
add_subdirectory(2-SlotCreation)
add_subdirectory(3-SlotInitialization)
add_subdirectory(4-SlotAssignment)
@ -10,3 +7,6 @@ add_subdirectory(6-FunctionDeclaration)
add_subdirectory(7-CallingObjectReference)
add_subdirectory(8-CallingObjectAssignment)
add_subdirectory(9-CallingObjectDeclaration)
add_subdirectory(10-CallingObjectInitialization)
add_subdirectory(11-AlternateSyntax)
add_subdirectory(12-Inheritance)

View File

@ -1,5 +1,4 @@
add_subdirectory(1-SeparateStartEndLines)
add_subdirectory(10-AfterKTHXBYE)
add_subdirectory(2-SeparateStartLineOnly)
add_subdirectory(3-SeparateEndLineOnly)
add_subdirectory(4-MustStartOnSeparateLine)
@ -8,3 +7,4 @@ add_subdirectory(6-BeginAfterLineSeparator)
add_subdirectory(7-EndBeforeLineSeparator)
add_subdirectory(8-IgnoreEmbeddedBTW)
add_subdirectory(9-BeforeHAI)
add_subdirectory(10-AfterKTHXBYE)

View File

@ -0,0 +1,6 @@
add_subdirectory(1-Output)
add_subdirectory(2-Initialization)
add_subdirectory(3-Assignment)
add_subdirectory(4-TypeInitialization)
add_subdirectory(5-Deallocation)
add_subdirectory(6-Functions)

View File

@ -7,3 +7,4 @@ add_subdirectory(6-Assignment)
add_subdirectory(7-AssignmentSameVariable)
add_subdirectory(8-TypeInitialization)
add_subdirectory(9-Deallocation)
add_subdirectory(10-Indirect)

View File

@ -1,6 +1,4 @@
add_subdirectory(1-Intergers)
add_subdirectory(10-ArityCheck)
add_subdirectory(11-OptionalAN)
add_subdirectory(2-FloatInteger)
add_subdirectory(3-IntegerFloat)
add_subdirectory(4-Floats)
@ -9,3 +7,5 @@ add_subdirectory(6-FloatIntegerStrings)
add_subdirectory(7-IntegerFloatStrings)
add_subdirectory(8-FloatStrings)
add_subdirectory(9-Nested)
add_subdirectory(10-ArityCheck)
add_subdirectory(11-OptionalAN)

View File

@ -1,4 +1,12 @@
add_subdirectory(1-Addition)
add_subdirectory(2-Subtraction)
add_subdirectory(3-Multiplication)
add_subdirectory(4-Division)
add_subdirectory(5-Modulo)
add_subdirectory(6-Maximum)
add_subdirectory(7-Minimum)
add_subdirectory(8-LogicalANDBinary)
add_subdirectory(9-LogicalORBinary)
add_subdirectory(10-LogicalXORBinary)
add_subdirectory(11-LogicalNOTUnary)
add_subdirectory(12-LogicalANDNary)
@ -8,11 +16,3 @@ add_subdirectory(15-Inequality)
add_subdirectory(16-Concatenation)
add_subdirectory(17-ExplicitCast)
add_subdirectory(18-ExplicitRecast)
add_subdirectory(2-Subtraction)
add_subdirectory(3-Multiplication)
add_subdirectory(4-Division)
add_subdirectory(5-Modulo)
add_subdirectory(6-Maximum)
add_subdirectory(7-Minimum)
add_subdirectory(8-LogicalANDBinary)
add_subdirectory(9-LogicalORBinary)

View File

@ -1,6 +1,4 @@
add_subdirectory(1-VoidWithSideEffects)
add_subdirectory(10-TooFewArguments)
add_subdirectory(11-EmptyBody)
add_subdirectory(2-ReturnValue)
add_subdirectory(3-VoidReturnValue)
add_subdirectory(4-NilReturnValue)
@ -9,3 +7,5 @@ add_subdirectory(6-DoubleRecursion)
add_subdirectory(7-ExpressionArguments)
add_subdirectory(8-VoidMustHaveNoArguments)
add_subdirectory(9-TooManyArguments)
add_subdirectory(10-TooFewArguments)
add_subdirectory(11-EmptyBody)

View File

@ -1,8 +1,5 @@
add_subdirectory(0-Benchmarks)
add_subdirectory(1-Structure)
add_subdirectory(10-Loops)
add_subdirectory(11-Unicode)
add_subdirectory(12-Arrays)
add_subdirectory(2-Comments)
add_subdirectory(3-Types)
add_subdirectory(4-Output)
@ -11,3 +8,6 @@ add_subdirectory(6-Variables)
add_subdirectory(7-Operators)
add_subdirectory(8-Conditionals)
add_subdirectory(9-Functions)
add_subdirectory(10-Loops)
add_subdirectory(11-Unicode)
add_subdirectory(12-Arrays)

View File

@ -0,0 +1,2 @@
INCLUDE(AddLolTest)
ADD_LOL_TEST(1-fopen OUTPUT test.out)

View File

@ -0,0 +1,7 @@
THE fog comes
on little cat feet.
It sits looking
over harbor and city
on silent haunches
and then moves on.

View File

@ -0,0 +1,10 @@
HAI 1.4
CAN HAS STDIO?
I HAS A file
file R I IZ STDIO'Z FOPENIN YR "read.dat" AN YR "r" MKAY
file R I IZ STDIO'Z FOPENIN YR "write.dat" AN YR "w" MKAY
file R I IZ STDIO'Z FOPENIN YR "write.dat" AN YR "a" MKAY
file R I IZ STDIO'Z FOPENIN YR "write.dat" AN YR "r+" MKAY
file R I IZ STDIO'Z FOPENIN YR "write.dat" AN YR "w+" MKAY
file R I IZ STDIO'Z FOPENIN YR "write.dat" AN YR "a+" MKAY
KTHXBYE

View File

@ -0,0 +1 @@
add_subdirectory(1-fopen)

View File

@ -0,0 +1 @@
add_subdirectory(1-stdio)

View File

@ -0,0 +1 @@
add_subdirectory(13-Bindings)

View File

@ -1 +1,2 @@
add_subdirectory(1.3-Tests)
add_subdirectory(1.4-Tests)

View File

@ -319,15 +319,6 @@ Token **tokenizeLexemes(LexemeList *list)
token = createToken(TT_BOOLEAN, "WIN", fname, line);
token->data.i = 1;
}
/* CAN HAS STDIO? */
else if (n < list->num - 2
&& !strcmp(lexeme->image, "CAN")
&& !strcmp(list->lexemes[n + 1]->image, "HAS")
&& !strcmp(list->lexemes[n + 2]->image, "STDIO?")) {
n += 2;
/* Just for fun; not actually in spec */
continue;
}
/* Newline */
/* Note that the spec is unclear as to whether a command *must*
* follow a comma. For now, we let commas end a line. */

View File

@ -104,6 +104,8 @@ typedef enum {
TT_OHAIIM, /**< Alternate array declaration. */
TT_IMLIEK, /**< Alternate inherited object declaration. */
TT_KTHX, /**< End of alternate array declaration. */
TT_CANHAS, /**< Library import declaration. */
TT_QUESTION, /**< End of library import declaration. */
TT_ENDOFTOKENS /**< Sentinel end of this enum -- don't move it! */
} TokenType;
@ -156,12 +158,12 @@ static const char *keywords[] = {
"SMOOSH", /* TT_SMOOSH */
"!", /* TT_BANG */
"GIMMEH", /* TT_GIMMEH */
"O RLY?", /* TT_ORLY */
"O RLY", /* TT_ORLY */
"YA RLY", /* TT_YARLY */
"MEBBE", /* TT_MEBBE */
"NO WAI", /* TT_NOWAI */
"OIC", /* TT_OIC */
"WTF?", /* TT_WTF */
"WTF", /* TT_WTF */
"OMG", /* TT_OMG */
"OMGWTF", /* TT_OMGWTF */
"GTFO", /* TT_GTFO */
@ -181,6 +183,8 @@ static const char *keywords[] = {
"O HAI IM", /* TT_OHAIIM */
"IM LIEK", /* TT_IMLIEK */
"KTHX", /* TT_KTHX */
"CAN HAS", /* TT_CANHAS */
"?", /* TT_QUESTION */
"" /* TT_ENDOFTOKENS */
};

View File

@ -19815,6 +19815,7 @@ static const char *names[] = {
"TRUMPET",
"TUGRIK SIGN",
"TULIP",
"TURKISH LIRA SIGN",
"TURNED AMPERSAND",
"TURNED ANGLE",
"TURNED BLACK SHOGI PIECE",
@ -41559,6 +41560,7 @@ static const long codepoints[] = {
0x1F3BA,
0x20AE,
0x1F337,
0x20BA,
0x214B,
0x29A2,
0x26CA,
@ -43488,7 +43490,7 @@ static const long codepoints[] = {
0x200B
};
#define NUM_UNICODE 21741
#define NUM_UNICODE 21742
/**
* Performs a binary search on an array of strings.