Skip to content

Instantly share code, notes, and snippets.

@mjschutz
Created January 24, 2018 04:54
Show Gist options
  • Save mjschutz/e3824d57b0635ebb49fcc79704221339 to your computer and use it in GitHub Desktop.
Save mjschutz/e3824d57b0635ebb49fcc79704221339 to your computer and use it in GitHub Desktop.
C version of scratch-lang from scratch-lang.notimetoplay.org
// 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