PDA

View Full Version : Sol programming language.



Aereshaa_the_2nd
2008-04-21, 12:50 AM
A few days ago, I got bored, and started designing a programming language.

Sol is a stack-oriented language. Statements in Sol act on the stack, pushing things on or popping them off, and often both.
The stack is a variable-length array of pointer-sized integers, so that both pointers and integers can be stored. The elements can be larger than a pointer's size on a given architecture, but must not be smaller.

Sol supports 1 data type: pointer-sized integers. All other data types are implemented as pointers. The ones supported by the standard instructions are:
-strings
-variable-length arrays
-bignums
-fractions

Sol scripts consist of lists of 1-4 letter instructions. Instructions can take one operand; all others must be popped off the stack. What follows is a list of instructions, in three groups: Core Sol Instructions, which must not be implemented as macros, Standard Sol Instructions, which may be implemented as macros. Any Sol instructions other than these must be implemented as macros.
Core Sol Instructions

n :: push number n onto the stack.
'c :: push character c onto the stack.
"s" :: push string s onto the stack.

k :: pop sthg off stack, do nothing else.

ic :: input character onto the stack.
in :: input number onto the stack.
is :: input string onto the stack.

oc :: output character off the stack.
on :: output number off the stack.
os :: output string off the stack.

+ :: 1 2 + =:= 2 + 1
- :: 1 2 - =:= 2 - 1
* :: 1 2 * =:= 2 * 1
/ :: 2 1 / =:= 1 / 2

sc :: pop string off stack, duplicate it, push string and copy of string.
sk :: pop two strings, concatenate them, and push result.
smcsc :: push string s, with delimiters c.
sr/regex/ :: pop a string, push how many times it matches regex.

x :: pop off a string, execute it in Sol.
?f?g? :: pop a number. If it is 0, execute f, otherwise execute g.
{f} :: pop a number, push it back. Until it is 0, execute f, then check again.

///will do Standard later///

So, what do you think? I tried to make a concise language, without it being too esoteric.

Ivius
2008-04-21, 09:31 AM
Designing is the easy part; now someone has to implement it. :smallamused:

EDIT: Did a quick search on SourceForge; already taken.
http://sourceforge.net/projects/sol/

Aereshaa_the_2nd
2008-04-21, 02:46 PM
^I've made an interpreter in C, except for the {f} statement, which has problems with using x inside it. I eventually plan to make a compiler which outputs C. I don't have time to explain it now, so I'll just dump the source.

#include "stdio.h"
#include "ctype.h"
#include "stdlib.h"
#include "string.h"

void push(long* stack,long val){
long len = stack[0];
stack = realloc(stack,len * 4 + 4);
stack[len] = val;
stack[0] = len + 1;
}

long pop(long* stack){
long len = stack[0];
long ret;
if(len == 0)ret = 0;
else{
ret = stack[len - 1];
len--;
stack = realloc(stack,len * 4);
stack[0] = len;
}
return ret;
}

char tonum(char c, char b){
if(isdigit(c)){
return c - '0';}
if(b){
if(isupper(c)){
return c - 'A' + 10;}
if(islower(c)){
return c - 'a' + 10;}
}
}

char pushnum(FILE* f, char c1, long* stack){
long tot = 0;
char c = c1;
while(!isdigit(c)){
c = getc(f);
}
while(isdigit(c)){
tot *= 10;
tot += tonum(c,0);
c = getc(f);
}
push(stack,tot);
return c;
}

void outstr(FILE* f, long *stack){
char* s = (char *) pop(stack);
int i = 0;
while(s[i] != 0){
putc(s[i],f);
i++;
}
fflush(f);
free(s);
}

void plus(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a + b);
}

void minus(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a - b);
}

void times(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a * b);
}

void divide(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a / b);
}

void modulus(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a % b);
}

void tofront(long* stack, FILE* f){
pushnum(f,'0',stack);
long p = pop(stack);
long len = stack[0];
push(stack,stack[len - 1 - p]);
}

long lngthstr(char* s){
long i = 0;
while(s[i] != 0)i++;
return i;
}

void copystr(long* stack){
long len = stack[0];
char* s = (char *) stack[len - 1];
long l = lngthstr(s);
char* z = malloc(l);
long i = 0;
//z[l] = 0;
while(s[i] != 0){
z[i] = s[i]; i++;
}
z[i] = 0;
push(stack, (long) z);
}

void pushstr(FILE* f, long* stack, char end){
char c;
char* s = malloc(1);
int i = 0;
c = getc(f);
while(c != end){//processes \ escapes.
if(end == '\"'){if(c == '\\'){
char d = getc(f);
if(d == 'n')c = '\n';
else if(d == 'd')c = '\177';
else if(d == 'e')c = '\033';
else if(d == 't')c = '\t';
else if(d == 'x'){
char d1 = getc(f);
char d2 = getc(f);
d1 = tonum(d1,1);
d2 = tonum(d2,1);
c = d1 * 16 + d2;}
else c = d;
}}
s[i] = c;
i++;
s = realloc(s, i + 1);
c = getc(f);
}
s[i] = 0;
push(stack, (long) s);
}

/*exec functis = (char *) pop(stack);
char* z = (char *) pop(stack);on. basis for if, while, and xk functions.*/

char* exec(char* s, long* stack){
FILE* ifl = fopen(".exec.#","w");
fputs(s,ifl);
fclose(ifl);
ifl = fopen(".exec.#","r");

/*copy from main*/
int com = getc(ifl);
while(com != EOF){

if(isdigit(com))com = pushnum(ifl, com, stack);

if(com == '\'')push(stack,getc(ifl));

if(com == '\"')pushstr(ifl,stack,'\"');

if(com == 'f')pushstr(ifl,stack,'\"');

if(com == 'o'){
char t = getc(ifl);
if(t == 's')outstr(stdout,stack);
if(t == 'c')putc(pop(stack),stdout);
if(t == 'n')printf("%d",pop(stack));
}

if(com == '+')plus(stack);

if(com == '-')minus(stack);

if(com == '*')times(stack);

if(com == '/')divide(stack);

if(com == '%')modulus(stack);

if(com == '#')tofront(stack,ifl);

if(com == 'i'){
char t = getc(ifl);
if(t == 's')pushstr(stdin,stack,'\n');
if(t == 'c')push(stack,getc(stdin));
if(t == 'n')pushnum(stdin, getc(stdin), stack);
}

if(com == 'x'){char* s = (char *) pop(stack); free(exec(s,stack));}

if(com == 'k')pop(stack);

if(com == '?')qmrk(stack,ifl);

if(com == '{')mprsand(stack,ifl);

if(com == 's'){
char t = getc(ifl);
if(t == 'c')copystr(stack);
if(t == 'm'){char c = getc(ifl); pushstr(ifl,stack,c);}
//if(t == 'k')catenate(stack);
//if(t == 'p')kmprstr(stack);
}

com = getc(ifl);
}
fclose(ifl);
/*end of copy*/
return s;
}

void qmrk(long* stack, FILE* f){
long cond = pop(stack);
pushstr(f, stack, '?');
pushstr(f, stack, '?');
if(cond){
free(exec( (char *) pop(stack), stack));
}else{
free( (char *) pop(stack));
free(exec( (char *) pop(stack), stack));
}
}

void mprsand(long* stack, FILE* f){
long cond = pop(stack);
pushstr(f, stack, '}');
char* s = (char *) pop(stack);
start:
push(stack, cond);
exec(s, stack);
cond = pop(stack);
if(cond != 0)goto start;
free(s);
}

int main(int argc,char** argv){
FILE* ifl = fopen(argv[1],"r");
long* stack = malloc(4);
stack[0] = 1;

int com = getc(ifl);
while(com != EOF){

if(isdigit(com))com = pushnum(ifl, com, stack);

if(com == '\'')push(stack,getc(ifl));

if(com == '\"')pushstr(ifl,stack,'\"');

if(com == 'f')pushstr(ifl,stack,'\"');

if(com == 'o'){
char t = getc(ifl);
if(t == 's')outstr(stdout,stack);
if(t == 'c')putc(pop(stack),stdout);
if(t == 'n')printf("%d",pop(stack));
}

if(com == '+')plus(stack);

if(com == '-')minus(stack);

if(com == '*')times(stack);

if(com == '/')divide(stack);

if(com == '%')modulus(stack);

if(com == '#')tofront(stack,ifl);

if(com == 'i'){
char t = getc(ifl);
if(t == 's')pushstr(stdin,stack,'\n');
if(t == 'c')push(stack,getc(stdin));
if(t == 'n')pushnum(stdin, getc(stdin), stack);
}

if(com == 'x'){char* s = (char *) pop(stack); free(exec(s,stack));}

if(com == 'k')pop(stack);

if(com == '?')qmrk(stack,ifl);

if(com == '{')mprsand(stack,ifl);

if(com == 's'){
char t = getc(ifl);
if(t == 'c')copystr(stack);
if(t == 'm'){char c = getc(ifl); pushstr(ifl,stack,c);}
//if(t == 'k')catenate(stack);
//if(t == 'p')kmprstr(stack);
}

com = getc(ifl);
}
fclose(ifl);
free(stack);
}

Ivius
2008-04-21, 05:38 PM
Woah! Writing a programming language and getting it to compile is awesome in my book. "x" also makes it easy to be self hosting; I can see a lisp-style self-compiler/interpreter being fairly easy. I'm thinking about writing a perl style "Type in commands, have them executed" type program in sol. Also, maybe at the end an 'else' that stops it and throws out a syntax error (Right now, comments are just like INTERCAL). But in the mean time:


"What's your name? "
os
is
"Hello, "
os
os

Aereshaa_the_2nd
2008-04-21, 11:26 PM
^ That's the first program my dad thought of too! However, I would output "\n" afterward; newlines are not passed in for the same reason that there isn't a '\"' at the end of each line included in the source: I use the same function with a different char end.

Also, I added a ; instruction which basically reads until a newline, discarding everything. That should do until I make some nestable comments, (tried it for a custom preprocessor once: NOT FUN:smallannoyed:).

Ivius
2008-04-22, 07:44 PM
Just for the heck of it, (with the help of a few spare hours) I tried to write a quick Sol interpreter in Perl (I've been learning it for a few weeks now). It actually went pretty well; I have it chomp the instructions, then split them into an array which I can use a big if elsif else block to figure out what to do with. I had a bunch of trouble with sr// (I just couldn't think of an algorithm for it) and sm (I didn't really know what you meant, and it wasn't in your interpreter) I'll probably only need about another solid hour of debugging before it can be usable. This could make Sol the language to have two implementations the earliest in its lifetime :smallamused:.

EDIT: I can't believe I forgot Linus' Law:

#!/usr/bin/perl
chomp(@cmds = <>);
@cmds = prepare(join " ", @cmds);

&run($_) for @cmds;

sub run { #exec, eval, do, and every other name is already taken by Perl. Thanks, Larry! >:(
my(@stack);

if ($_[0] =~ /(\d+)|"(\w*\s*\w*)"|'(.)/) { #All three work the same...
push @stack, $1 . $2 . $3;
}
elsif ($_[0] == "k") {
pop @stack;
}
elsif ($_[0] == "ic") {
push @stack, getc;
}
elsif ($_[0] == "in") {
my($num) = <>;
push @stack, $1 if $num =~ /(\d+)/
}
elsif ($_[0] == "is") {
my($str) = <>;
push @stack, $1 if $str =~ /"(\w*\s*\w*)"/;
}
#Deja vu...
elsif ($_[0] == "oc") {
my($char) = pop @stack;
print $char if $char =~ /./;
}
elsif ($_[0] == "on") {
my($num) = pop @stack;
print $num if $num =~ /\d+/;
}
elsif ($_[0] == "os") {
my($str) = pop @stack;
print $str if $str =~ /\w*\s*\w*/;
}
elsif ($_[0] == "+") {
push @stack, pop(@stack) + pop(@stack);
}
elsif ($_[0] == "-") {
push @stack, pop(@stack) - pop(@stack);
}
elsif ($_[0] == "*") {
push @stack, pop(@stack) * pop(@stack);
}
elsif ($_[0] == "/") {
push @stack, pop(@stack) / pop(@stack);
}
elsif ($_[0] == "sc") {
my($str) = pop @stack;
push @stack, $str;
push @stack, $str;
}
elsif ($_[0] == "sk") {
push @stack, pop(@stack) . pop(@stack);
}
elsif ($_[0] == "x") {
&run(prepare(pop(@stack)));
}
elsif ($_[0] =~ /\?(.+)\?(.+)\?/) {
my(@nonzerocmds) = prepare $1;
my(@zerocmds) = prepare $2;
unless (pop @stack == 0) {
&run($_) for @nonzerocmds;
}
else {
&run($_) for @zerocmds;
}
}
}


sub prepare { #Turns a string of commands with arbitrary whitespace into an array of commands
my(@cmdarray) = split ' ', $_[0];
chomp for @cmdarray;
}

Aereshaa_the_2nd
2008-04-22, 10:41 PM
^Heh, yeah. "Given enough hackers, all bugs are obvious". You made an implementation in Perl? That's.. wow, I guess it's true about volunteer work being the best way to develop! Two implementations of basic Sol in 4 days after I started! Also, I fixed the {} problem. It was because exec was storing it in a fixed filename. It now randomly generates filenames, and deletes the files when it's done.

For sr I simply used this (http://opengroup.org/onlinepubs/007908799/xsh/regex.h.html). :smallbiggrin: Meh, I don't like reinventing the wheel.
For sm,

if(t == 'm'){char c = getc(ifl); pushstr(ifl,stack,c);} That's inside if(com == 's'). It basically implements custom delimiters.

EDIT: your perl fails with this error:
Can't call method "run" without a package or object reference at ./sol.perl line 7, <> line 3.
In other words, perl fails at:
chomp(@cmds = <>);
I don't know perl all that well, but tell me if you know what went wrong there.

EDIT2: I added a preprocessor which allows definitions of new instructions, with the M instruction:
Mfname]"code" x
after it, all instances of fname are replaced by "code" x

Ivius
2008-04-23, 03:38 PM
EDIT: your perl fails with this error:
Can't call method "run" without a package or object reference at ./sol.perl line 7, <> line 3.
In other words, perl fails at:
chomp(@cmds = <>);
I don't know perl all that well, but tell me if you know what went wrong there.

Yeah, Perl functions have more exceptions than English. I fixed that; now it starts and takes input but I can't tell what else it's doing because there's no output. It'll be up someday...

Aereshaa_the_2nd
2008-04-23, 10:34 PM
Hokai. Sounds a lot like trying to use ncurses to me. (Dam those obscure formatting sequences :smallannoyed: )

Ivius
2008-04-25, 04:51 PM
C isn't really my forte, but I might be able to code up the equivalent of an "include" instruction (or adapt the one from another open source language written in C, like python or perl). It'd make libraries possible (otherwise the standard library would be at the top of every sol program :smallamused:).I'd probably need the updated source code with macros and comments, though.

Aereshaa_the_2nd
2008-04-26, 06:33 PM
Ok, here it is:

#include "stdio.h"
#include "ctype.h"
#include "stdlib.h"
#include "string.h"

void push(long* stack,long val){
long len = stack[0];
stack = realloc(stack,len * 4 + 4);
stack[len] = val;
stack[0] = len + 1;
}

long pop(long* stack){
long len = stack[0];
long ret;
if(len == 0)ret = 0;
else{
ret = stack[len - 1];
len--;
stack = realloc(stack,len * 4);
stack[0] = len;
}
return ret;
}

char tonum(char c, char b){
if(isdigit(c)){
return c - '0';}
if(b){
if(isupper(c)){
return c - 'A' + 10;}
if(islower(c)){
return c - 'a' + 10;}
}
}

char pushnum(FILE* f, char c1, long* stack){
long tot = 0;
char c = c1;
while(!isdigit(c)){
c = getc(f);
}
while(isdigit(c)){
tot *= 10;
tot += tonum(c,0);
c = getc(f);
}
push(stack,tot);
return c;
}

void outstr(FILE* f, long *stack){
char* s = (char *) pop(stack);
int i = 0;
while(s[i] != 0){
putc(s[i],f);
i++;
}
fflush(f);
free(s);
}

void plus(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a + b);
}

void minus(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a - b);
}

void times(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a * b);
}

void divide(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a / b);
}

void modulus(long* stack){
long a = pop(stack);
long b = pop(stack);
push(stack, a % b);
}

void tofront(long* stack, FILE* f){
pushnum(f,'0',stack);
long p = pop(stack);
long len = stack[0];
push(stack,stack[len - 1 - p]);
}

long lngthstr(char* s){
long i = 0;
while(s[i] != 0)i++;
return i;
}

void copystr(long* stack){
long len = stack[0];
char* s = (char *) stack[len - 1];
char* z = malloc(lngthstr(s));
long i = 0;
//z[l] = 0;
while(s[i] != 0){
z[i] = s[i]; i++;
}
z[i] = 0;
push(stack, (long) z);
}

void pushstr(FILE* f, long* stack, char end){
char c;
char* s = malloc(1);
int i = 0;
c = getc(f);
while(c != end){//processes \ escapes.
if(end == '\"'){if(c == '\\'){
char d = getc(f);
if(d == 'n')c = '\n';
else if(d == 'd')c = '\177';
else if(d == 'e')c = '\033';
else if(d == 't')c = '\t';
else if(d == 'x'){
char d1 = getc(f);
char d2 = getc(f);
d1 = tonum(d1,1);
d2 = tonum(d2,1);
c = d1 * 16 + d2;}
else c = d;
}}
s[i] = c;
i++;
s = realloc(s, i + 1);
c = getc(f);
}
s[i] = 0;
push(stack, (long) s);
}

/*exec functis = (char *) pop(stack);
char* z = (char *) pop(stack);on. basis for if, while, and xk functions.*/

char* exec(char* s, long* stack){
FILE* ifl = tmpfile();
fputs(s,ifl);
rewind(ifl);
/*copy from main*/
int com = getc(ifl);
while(com != EOF){

if(isdigit(com))com = pushnum(ifl, com, stack);

if(com == '\'')push(stack,getc(ifl));

if(com == '\"')pushstr(ifl,stack,'\"');

if(com == 'f')pushstr(ifl,stack,'\"');

if(com == 'o'){
char t = getc(ifl);
if(t == 's')outstr(stdout,stack);
if(t == 'c')putc(pop(stack),stdout);
if(t == 'n')printf("%d",pop(stack));
}

if(com == '+')plus(stack);

if(com == '-')minus(stack);

if(com == '*')times(stack);

if(com == '/')divide(stack);

if(com == '%')modulus(stack);

if(com == '#')tofront(stack,ifl);

if(com == 'i'){
char t = getc(ifl);
if(t == 's')pushstr(stdin,stack,'\n');
if(t == 'c')push(stack,getc(stdin));
if(t == 'n')pushnum(stdin, getc(stdin), stack);
}

if(com == 'x'){char* s = (char *) pop(stack); free(exec(s,stack));}

if(com == 'k')pop(stack);

if(com == '?'){
long cond = pop(stack);
pushstr(ifl, stack, '?');
pushstr(ifl, stack, '?');
char* s = (char *) pop(stack);
char* c = (char *) pop(stack);
if(cond > 0) exec(s,stack);
else exec(c,stack);
free(s);
free(c);
}

//if(com == '{')mprsand(stack,ifl);

if(com == 's'){
char t = getc(ifl);
if(t == 'c')copystr(stack);
if(t == 'm'){char c = getc(ifl); pushstr(ifl,stack,c);}
//if(t == 'k')catenate(stack);
//if(t == 'p')kmprstr(stack);
}

com = getc(ifl);
}

fclose(ifl);
/*end of copy*/
return s;
}

/*
void mprsand(long* stack, FILE* f){
long cond = pop(stack);
pushstr(f, stack, '}');
char* s = (char *) pop(stack);
start:
push(stack, cond);
exec(s, stack);
cond = pop(stack);
if(cond != 0)goto start;
free(s);
}*/

int main(int argc,char** argv){
//FILE* ihl = fopen(argv[1],"r");
FILE* ifl = /* tmpfile();*/ fopen(argv[1],"r");

char** macs = malloc(128 * sizeof (char *));


long* stack = malloc(4);
stack[0] = 1;


int com = getc(ifl);
while(com != EOF){

if(isdigit(com))com = pushnum(ifl, com, stack);

if(com == '\'')push(stack,getc(ifl));

if(com == '\"')pushstr(ifl,stack,'\"');

if(com == 'f')pushstr(ifl,stack,'\"');

if(com == 'M'){
char c = getc(ifl);
pushstr(ifl,stack,'M');
macs[c] = (char *) pop(stack);}

if(com == 'm'){
char c = getc(ifl);
exec(macs[c],stack);
}

if(com == 'o'){
char t = getc(ifl);
if(t == 's')outstr(stdout,stack);
if(t == 'c')putc(pop(stack),stdout);
if(t == 'n')printf("%d",pop(stack));
}

if(com == ';'){
int c = getc(ifl);
while(c != '\n')c = getc(ifl);
}

if(com == '+')plus(stack);

if(com == '-')minus(stack);

if(com == '*')times(stack);

if(com == '/')divide(stack);

if(com == '%')modulus(stack);

if(com == '#')tofront(stack,ifl);

if(com == 'i'){
char t = getc(ifl);
if(t == 's')pushstr(stdin,stack,'\n');
if(t == 'c')push(stack,getc(stdin));
if(t == 'n')pushnum(stdin, getc(stdin), stack);
}

if(com == 'x'){char* s = (char *) pop(stack); free(exec(s,stack));}

if(com == 'k')pop(stack);

if(com == '?'){
long cond = pop(stack);
pushstr(ifl, stack, '?');
pushstr(ifl, stack, '?');
char* s = (char *) pop(stack);
char* c = (char *) pop(stack);
if(cond > 0) exec(s,stack);
else exec(c,stack);
free(s);
free(c);
}

//if(com == '{')mprsand(stack,ifl);

if(com == 's'){
char t = getc(ifl);
if(t == 'c')copystr(stack);
if(t == 'm'){char c = getc(ifl); pushstr(ifl,stack,c);}
//if(t == 'k')catenate(stack);
//if(t == 'p')kmprstr(stack);
//if(t == 'r'){}
}

com = getc(ifl);
}

fclose(ifl);
free(stack);
}
I didn't include the regexes, they're proving to be full of heisenbugs.