Created
January 24, 2018 04:54
-
-
Save mjschutz/e3824d57b0635ebb49fcc79704221339 to your computer and use it in GitHub Desktop.
C version of scratch-lang from scratch-lang.notimetoplay.org
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// C version of | |
// Part 1: numbers and words — DRAFT 2 — 2008-09-06 | |
// http://scratch-lang.notimetoplay.org/scratch-lang.js | |
#include <ctype.h> | |
#include <string.h> | |
#include <math.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
typedef struct _stack { | |
double value; | |
struct _stack* next; | |
} stack_t; | |
int stack_count(stack_t* stack) { | |
int count = 0; | |
if (!stack) return 0; | |
while (stack != NULL) { | |
count++; | |
stack = stack->next; | |
} | |
return count; | |
} | |
void stack_print(stack_t* stack) { | |
if (!stack) return; | |
while (stack != NULL) { | |
printf("%lf\n", stack->value); | |
stack = stack->next; | |
} | |
} | |
void stack_push(stack_t** stack, double value) { | |
if (!stack) return; | |
stack_t* stack_top = malloc(sizeof(stack_t)); | |
stack_top->value = value; | |
stack_top->next = *stack; | |
*stack = stack_top; | |
} | |
int stack_pop(stack_t** stack, double* value) { | |
if (!value || !stack || !*stack) return 0; | |
stack_t* stk = *stack; | |
*value = stk->value; | |
*stack = stk->next; | |
free(stk); | |
return 1; | |
} | |
void stack_free(stack_t* stack) { | |
while (stack) { | |
stack_t* next = stack->next; | |
free(stack); | |
stack = next; | |
} | |
} | |
struct _context; | |
typedef int (*dict_call_t)(struct _context* ctx); | |
typedef struct _dict { | |
char* name; | |
dict_call_t call; | |
struct _dict* next; | |
} dict_t; | |
int on_dictionary(dict_t* dict, char* name) { | |
if (!dict) return 0; | |
while (dict) { | |
if (!strcmp(dict->name, name)) | |
return 1; | |
dict = dict->next; | |
} | |
return 0; | |
} | |
void dict_add(dict_t** dict, char* name, dict_call_t call) { | |
if (!dict) return; | |
dict_t* dt = malloc(sizeof(dict_t)); | |
dt->name = name; | |
dt->call = call; | |
dt->next = *dict; | |
*dict = dt; | |
} | |
dict_call_t dict_call(dict_t* dict, char* name) { | |
if (!dict) return NULL; | |
while (dict) { | |
if (!strcmp(dict->name, name)) | |
return dict->call; | |
dict = dict->next; | |
} | |
return NULL; | |
} | |
void dict_free(dict_t* dict) { | |
while (dict) { | |
dict_t* next = dict->next; | |
free(dict); | |
dict = next; | |
} | |
} | |
typedef struct _context { | |
stack_t* stack; | |
dict_t* dict; | |
} context_t; | |
char** scratch_lexer(char* text) { | |
char** words = NULL; | |
char* ch; | |
int wordCount = 0, pos = 0; | |
if (!text) return NULL; | |
for (ch=text; *ch; ch++) { | |
int count =0; | |
while (*ch && isspace(*ch)) ch++; // skip whitespace | |
while (*ch && !isspace(*ch)) { count=1; ch++; } // skip word | |
if (count) wordCount++; | |
} | |
if (!wordCount) return NULL; | |
words = malloc((wordCount+1)*sizeof(char*)); | |
for (ch=text; *ch; ch++) { | |
int count =0; | |
char *start; | |
while (*ch && isspace(*ch)) ch++; // skip whitespace | |
start = ch; | |
while (*ch && !isspace(*ch)) { count++; *ch = toupper(*ch); ch++; } // skip word | |
if (count) wordCount++; | |
words[pos] = malloc((count)*sizeof(char)); | |
memset(words[pos], 0, (count)*sizeof(char)); | |
strncpy(words[pos++], start, count); | |
} | |
words[pos] = NULL; | |
return words; | |
} | |
void scratch_lexer_free(char** words) { | |
int i; | |
if (words) { | |
for (i = 0; words[i]; i++) { | |
free(words[i]); | |
} | |
free(words); | |
} | |
} | |
int is_number(char* word) { | |
int dig = 1; | |
int dot = 0; | |
char *ch = word; | |
while (*ch) { | |
if (*ch == '.') { | |
dot++; | |
} else if (!isdigit(*ch)) { | |
dig = 0; | |
break; | |
} | |
ch++; | |
} | |
return dot <= 1 && dig; | |
} | |
// Print and discard top of stack. | |
int print(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 1) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0; | |
if (stack_pop(&ctx->stack, &value)) { | |
printf("%lf\n", value); | |
return 1; | |
} | |
return 0; | |
} | |
// Print out the contents of the stack. | |
int print_stack(context_t* ctx) { | |
if (ctx) { | |
stack_print(ctx->stack); | |
return 1; | |
} | |
return 0; | |
} | |
int math_add(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2)) { | |
stack_push(&ctx->stack, value+value2); | |
return 1; | |
} | |
return 0; | |
} | |
int math_sub(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2)) { | |
stack_push(&ctx->stack, value-value2); | |
return 1; | |
} | |
return 0; | |
} | |
int math_mul(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2)) { | |
stack_push(&ctx->stack, value*value2); | |
return 1; | |
} | |
return 0; | |
} | |
int math_div(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2)) { | |
stack_push(&ctx->stack, value/value2); | |
return 1; | |
} | |
return 0; | |
} | |
int math_sqrt(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 1) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0; | |
if (stack_pop(&ctx->stack, &value)) { | |
stack_push(&ctx->stack, sqrt(value)); | |
return 1; | |
} | |
return 0; | |
} | |
// Duplicate the top of stack (TOS). | |
int stack_dup(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 1) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0; | |
if (stack_pop(&ctx->stack, &value)) { | |
stack_push(&ctx->stack, value); | |
stack_push(&ctx->stack, value); | |
return 1; | |
} | |
return 0; | |
} | |
// Throw away the TOS -- the opposite of DUP. | |
int stack_drop(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 1) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0; | |
if (stack_pop(&ctx->stack, &value)) { | |
return 1; | |
} | |
return 0; | |
} | |
// Exchange positions of TOS and second item on stack (2OS). | |
int stack_swap(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2)) { | |
stack_push(&ctx->stack, value); | |
stack_push(&ctx->stack, value2); | |
return 1; | |
} | |
return 0; | |
} | |
// Copy 2OS on top of stack. | |
int stack_over(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2)) { | |
stack_push(&ctx->stack, value2); | |
stack_push(&ctx->stack, value); | |
stack_push(&ctx->stack, value2); | |
return 1; | |
} | |
return 0; | |
} | |
// Bring the 3rd item on stack to the top. | |
int stack_rot(context_t* ctx) { | |
if (!ctx) return 0; | |
if (stack_count(ctx->stack) < 2) { | |
printf("Not enough items on stack"); | |
return 0; | |
} | |
double value = 0, value2 = 0, value3 = 0; | |
if (stack_pop(&ctx->stack, &value) && stack_pop(&ctx->stack, &value2) && stack_pop(&ctx->stack, &value3)) { | |
stack_push(&ctx->stack, value2); | |
stack_push(&ctx->stack, value); | |
stack_push(&ctx->stack, value3); | |
return 1; | |
} | |
return 0; | |
} | |
int main(int argc, char** argv) { | |
char text[500]; | |
char** words; | |
int i; | |
fgets(text, 500, stdin); | |
words = scratch_lexer(text); | |
if (!words) | |
return -1; | |
context_t ctx; | |
ctx.stack = NULL; | |
ctx.dict = NULL; | |
dict_add(&ctx.dict, "PRINT", print); | |
dict_add(&ctx.dict, ".", print); | |
dict_add(&ctx.dict, "PSTACK", print_stack); | |
dict_add(&ctx.dict, ".S", print_stack); | |
dict_add(&ctx.dict, "+", math_add); | |
dict_add(&ctx.dict, "-", math_sub); | |
dict_add(&ctx.dict, "*", math_mul); | |
dict_add(&ctx.dict, "/", math_div); | |
dict_add(&ctx.dict, "SQRT", math_sqrt); | |
dict_add(&ctx.dict, "DUP", stack_dup); | |
dict_add(&ctx.dict, "DROP", stack_drop); | |
dict_add(&ctx.dict, "SWAP", stack_swap); | |
dict_add(&ctx.dict, "OVER", stack_over); | |
dict_add(&ctx.dict, "ROT", stack_rot); | |
for (i = 0; words[i]; i++) { | |
char* word = words[i]; | |
if (on_dictionary(ctx.dict, word)) { | |
dict_call_t caller = dict_call(ctx.dict, word); | |
if (caller) { | |
if (!caller(&ctx)) { | |
printf("Failed to call %s\n", word); | |
break; | |
} | |
} else { | |
printf("Unknown word on dictionary\n"); | |
break; | |
} | |
} else if (is_number(word)) { | |
stack_push(&ctx.stack, atof(word)); | |
} else { | |
printf("Unknown word %s\n", word); | |
break; | |
} | |
} | |
stack_free(ctx.stack); | |
dict_free(ctx.dict); | |
scratch_lexer_free(words); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment