├── 2 ├── vm ├── output ├── vm2 ├── lang │ ├── a.out │ ├── future │ ├── buildast │ ├── buildparser │ ├── queue.co │ ├── graph.h │ ├── lexer.l │ ├── io.c │ ├── como_parser.h │ ├── ast.c │ ├── grammar2.y │ ├── grammar.y │ └── como_lexer.h ├── leaktest ├── builtins.c ├── mm │ ├── mm.c │ └── Makefile ├── gc.c ├── Makefile ├── builtin_readline.c ├── program.c ├── como_debug.h ├── como_debug.c ├── simple.c ├── eval.c ├── vm.c └── vm2.c ├── test ├── add ├── assert ├── gc ├── print ├── this ├── var ├── assert2 ├── assignexpr ├── typeof2 ├── and ├── assign ├── len ├── expr ├── printfunction ├── typeof4 ├── typeofunarynot ├── for_loop ├── typeof3 ├── sym ├── return_val_unused ├── returnarray ├── unary ├── array ├── range.como ├── printarray ├── fizzbuzz ├── fact ├── array4 ├── stacktrace ├── typeof ├── even.como ├── func ├── first_class_functions ├── func_symbol_table ├── test.asm ├── slot ├── array2 ├── test_como_io.c └── recursiveprint ├── makedebug.sh ├── README.GRAMMAR ├── .gitignore ├── Makefile ├── README.STYLE_GUIDE ├── como_io.h ├── como_opcode.c ├── como_stack.h ├── como_globals.h ├── como_executor.h ├── como_stack.c ├── como_object.h ├── como_debug.c ├── como.c ├── como_compiler.h ├── como_debug.h ├── como_io.c ├── como_object.c ├── README.md ├── support └── .vimrc ├── como_ast_free.c ├── como_opcode.h ├── como_parser.h ├── como_lexer.l ├── como_executor.c ├── como_ast.h ├── como_ast.c ├── vm.c ├── como_parser.y └── como_lexer.h /vm/output: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/add: -------------------------------------------------------------------------------- 1 | 1+1; 2 | -------------------------------------------------------------------------------- /test/assert: -------------------------------------------------------------------------------- 1 | assert(0); 2 | -------------------------------------------------------------------------------- /test/gc: -------------------------------------------------------------------------------- 1 | print(typeof 1); 2 | -------------------------------------------------------------------------------- /test/print: -------------------------------------------------------------------------------- 1 | print(1); 2 | -------------------------------------------------------------------------------- /test/this: -------------------------------------------------------------------------------- 1 | this = 1 + 1; 2 | -------------------------------------------------------------------------------- /test/var: -------------------------------------------------------------------------------- 1 | a = ryan + 1; 2 | -------------------------------------------------------------------------------- /test/assert2: -------------------------------------------------------------------------------- 1 | assert(1 == 1); 2 | -------------------------------------------------------------------------------- /test/assignexpr: -------------------------------------------------------------------------------- 1 | expr = 1 + 5; 2 | -------------------------------------------------------------------------------- /test/typeof2: -------------------------------------------------------------------------------- 1 | print(typeof 1); 2 | -------------------------------------------------------------------------------- /test/and: -------------------------------------------------------------------------------- 1 | assert(0 == 0 && 0 != 1); 2 | -------------------------------------------------------------------------------- /test/assign: -------------------------------------------------------------------------------- 1 | a = "Ryan McCullagh"; 2 | -------------------------------------------------------------------------------- /test/len: -------------------------------------------------------------------------------- 1 | print(typeof len == "function"); 2 | -------------------------------------------------------------------------------- /test/expr: -------------------------------------------------------------------------------- 1 | array = []; 2 | 3 | print(array [ -1 ]); 4 | -------------------------------------------------------------------------------- /makedebug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | make CPPFLAGS=-DCOMO_DEBUG=1 3 | -------------------------------------------------------------------------------- /test/printfunction: -------------------------------------------------------------------------------- 1 | function fn() {} 2 | 3 | print(fn); 4 | -------------------------------------------------------------------------------- /vm/vm2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analang/como-lang-ng/HEAD/vm/vm2 -------------------------------------------------------------------------------- /test/typeof4: -------------------------------------------------------------------------------- 1 | type = typeof typeof 1; 2 | assert(type == "string"); 3 | -------------------------------------------------------------------------------- /test/typeofunarynot: -------------------------------------------------------------------------------- 1 | result = typeof !0; 2 | assert(result == "int"); 3 | -------------------------------------------------------------------------------- /vm/lang/a.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/analang/como-lang-ng/HEAD/vm/lang/a.out -------------------------------------------------------------------------------- /vm/lang/future: -------------------------------------------------------------------------------- 1 | A(arg1, arg2) { 2 | 3 | }; 4 | 5 | Init = (x, y) { 6 | print(x); 7 | } 8 | -------------------------------------------------------------------------------- /test/for_loop: -------------------------------------------------------------------------------- 1 | for(i = 5; i >= 0; i--) 2 | { 3 | if(i % 2 == 0) { 4 | print(i); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /vm/leaktest: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | valgrind \ 3 | --track-origins=yes \ 4 | --leak-check=full \ 5 | $@ 6 | -------------------------------------------------------------------------------- /test/typeof3: -------------------------------------------------------------------------------- 1 | function isUndefined(name) { 2 | return typeof _name; 3 | } 4 | 5 | print(isUndefined(1)); 6 | -------------------------------------------------------------------------------- /test/sym: -------------------------------------------------------------------------------- 1 | name = "Ryan"; 2 | full_name = name + " McCullagh"; 3 | a = 1; 4 | b = a; 5 | c = a + b; 6 | print(c); 7 | -------------------------------------------------------------------------------- /test/return_val_unused: -------------------------------------------------------------------------------- 1 | function fn() 2 | { 3 | return __FUNCTION__; 4 | } 5 | 6 | fn(); 7 | 8 | print(5 * 5); 9 | -------------------------------------------------------------------------------- /test/returnarray: -------------------------------------------------------------------------------- 1 | function getArray() { return [1,2,3,4]; } 2 | 3 | one = getArray()[0]; 4 | 5 | assert(one == 1); 6 | -------------------------------------------------------------------------------- /vm/builtins.c: -------------------------------------------------------------------------------- 1 | static como_object *como__builtin_readline(como_object *args) 2 | { 3 | #include "builtin_readline.c" 4 | } -------------------------------------------------------------------------------- /test/unary: -------------------------------------------------------------------------------- 1 | function unary(obj) 2 | { 3 | return !obj; 4 | } 5 | 6 | assert(unary(0) == 1); 7 | assert(unary(1) == 0); 8 | -------------------------------------------------------------------------------- /vm/lang/buildast: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CMD="gcc -Wall -Wextra -std=c89 -O0 -g -ggdb ast.c -o main $@" 3 | echo $CMD 4 | `$CMD` 5 | -------------------------------------------------------------------------------- /test/array: -------------------------------------------------------------------------------- 1 | function getArray() { 2 | a = [1,2,3]; 3 | return a; 4 | } 5 | 6 | print( 7 | getArray()[len(getArray()) - 1] 8 | ); 9 | -------------------------------------------------------------------------------- /test/range.como: -------------------------------------------------------------------------------- 1 | func print_range(a, b) { 2 | while(a <= b) { 3 | print(a); 4 | a++; 5 | } 6 | } 7 | 8 | print_range(0, 10); 9 | -------------------------------------------------------------------------------- /vm/lang/buildparser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -x 3 | bison -d grammar.y 4 | flex lexer.l 5 | gcc -Wall -Wextra grammar.tab.c como_lexer.c -o como 6 | -------------------------------------------------------------------------------- /test/printarray: -------------------------------------------------------------------------------- 1 | function printArray(value) { 2 | print(value); 3 | } 4 | 5 | printArray([1,2,3,4,"String", [ 6 | "This is an element of an inner array" 7 | ]]); 8 | -------------------------------------------------------------------------------- /vm/mm/mm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef struct _mem_header { 6 | unsigned int size; 7 | struct block *next; 8 | } mem_header; 9 | -------------------------------------------------------------------------------- /test/fizzbuzz: -------------------------------------------------------------------------------- 1 | function fizzBuzz() { 2 | for(i = 1; i <= 100; i++) { 3 | if(i % 3 == 0 && i % 5 == 0) { 4 | print("FizzBuzz"); 5 | } 6 | } 7 | } 8 | 9 | fizzBuzz(); 10 | -------------------------------------------------------------------------------- /test/fact: -------------------------------------------------------------------------------- 1 | func fact(n) 2 | { 3 | if(n==1) 4 | { 5 | return 1; 6 | } 7 | else 8 | { 9 | return n * fact(n-1); 10 | } 11 | } 12 | 13 | print(fact(5)); -------------------------------------------------------------------------------- /vm/gc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | como_object *gc_new(como_frame *frame, como_object *obj) 4 | { 5 | obj->next = frame->root; 6 | obj->flags = 0; 7 | 8 | frame->root = obj; 9 | 10 | return obj; 11 | } -------------------------------------------------------------------------------- /test/array4: -------------------------------------------------------------------------------- 1 | names = [ 2 | "Ryan", 3 | "McCullagh", 4 | "Hello", 5 | "World" 6 | ]; 7 | 8 | function len(a) { 9 | return 4; 10 | } 11 | 12 | for(i = 0; i < len(names); i++) { 13 | print(names[i]); 14 | } 15 | -------------------------------------------------------------------------------- /test/stacktrace: -------------------------------------------------------------------------------- 1 | function five() { a; } 2 | function four() { five(); } 3 | function three() { four(); } 4 | function two() { three(); } 5 | function one() { two(); } 6 | 7 | function init() { one(); } 8 | 9 | 10 | init(); 11 | -------------------------------------------------------------------------------- /README.GRAMMAR: -------------------------------------------------------------------------------- 1 | new grammar for calls (TODO): 2 | /* At compile time, check that the node type of expr is not a literal 3 | * Note: see como_parser.y for the expr production 4 | */ 5 | expr '(' optional_argument_list ')' 6 | 7 | -------------------------------------------------------------------------------- /test/typeof: -------------------------------------------------------------------------------- 1 | /** 2 | * evaluation order (typeof 1) == 1 3 | * "int" == 1 4 | * use typeof (1 == 1) to force precedence 5 | */ 6 | print(typeof 1 == 1); 7 | 8 | print(typeof(1==10)); 9 | 10 | 11 | function fn() { 12 | 13 | } 14 | 15 | print(typeof fn); 16 | -------------------------------------------------------------------------------- /test/even.como: -------------------------------------------------------------------------------- 1 | func iseven(num) { 2 | return num % 2 == 0; 3 | } 4 | 5 | func print_even(num) 6 | { 7 | while(num) { 8 | if(iseven(num)) { 9 | print(num + " is even"); 10 | } else { 11 | print(num + " is odd"); 12 | } 13 | num = num - 1; 14 | } 15 | } 16 | 17 | print_even(5); -------------------------------------------------------------------------------- /vm/lang/queue.co: -------------------------------------------------------------------------------- 1 | import system.logger 2 | import math; 3 | 4 | class Queue { 5 | 6 | public function Queue() { 7 | this.logger = logger.Log("Queue"); 8 | this.items = []; 9 | } 10 | 11 | public function clear() { 12 | this.items = []; 13 | } 14 | 15 | public function enqueue() { 16 | this.items.push(item); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/func: -------------------------------------------------------------------------------- 1 | func a() 2 | { 3 | /* This function will be defined inside of a's symbol table */ 4 | func b() 5 | { 6 | return __FUNCTION__; 7 | } 8 | 9 | return b; 10 | } 11 | 12 | first = a(); 13 | print(first()); 14 | 15 | func ref() { 16 | return __FUNCTION__; 17 | } 18 | 19 | _ref = ref; 20 | 21 | ref = 1; 22 | 23 | print(_ref()); 24 | print(ref); 25 | -------------------------------------------------------------------------------- /test/first_class_functions: -------------------------------------------------------------------------------- 1 | function name() { 2 | /* Note that __FUNCTION__ is defined at compile time, 3 | not runtime, so even if the function is proxied, it will 4 | still return the real name 5 | */ 6 | return __FUNCTION__; 7 | } 8 | 9 | func factory(callable) { 10 | return callable(); 11 | } 12 | 13 | name_proxy = name; 14 | 15 | 16 | /* name */ 17 | print(factory(name_proxy)); 18 | 19 | -------------------------------------------------------------------------------- /test/func_symbol_table: -------------------------------------------------------------------------------- 1 | func init(truth) 2 | { 3 | if(truth) 4 | { 5 | print(not_defined_yet); 6 | } 7 | else 8 | { 9 | not_defined_yet = 1; 10 | } 11 | } 12 | 13 | /* Make sure to clear the symbol table on the way out of the funciton */ 14 | init(0); 15 | /* Because this call will actually print 1, even though the name isn't 16 | defined yet 17 | */ 18 | init(1); 19 | -------------------------------------------------------------------------------- /test/test.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; print(typeof 1); 3 | ; 4 | 5 | 1 PUSH 1 ; push 1 onto the stack 6 | 2 TYPEOF ; pop the stack, determine the object type, and push result 7 | 3 PRINT ; pop the stack, and print the object 8 | 4 IRETURN 0 ; return with no value, 0 indiciates there is no return value to be popped from the stack 9 | 5 HALT ; end execution\ 10 | 11 | 12 | 0 newObject(1) 13 | pop, got object 0 14 | push newString (1) 15 | POP, got object 1 16 | -------------------------------------------------------------------------------- /vm/mm/Makefile: -------------------------------------------------------------------------------- 1 | OS_TYPE := $(shell sh -c 'uname -s') 2 | COMO_OBJECT_LIB=-lcomoobject 3 | COMO_OBJECT_LIB_LD_FLAGS= -shared 4 | BINDIR = $(HOME)/objlib/bin 5 | CFLAGS = -Wall -Wextra -g -ggdb -std=c99 -I$(HOME)/objlib/src -L$(BINDIR) -Wl,-rpath=$(BINDIR) 6 | LIBS := $(COMO_OBJECT_LIB) 7 | SOURCES = $(shell echo *.c) 8 | COMO_OBJECT_LIB_OBJECTS =vm2 9 | 10 | all: $(COMO_OBJECT_LIB_OBJECTS) 11 | 12 | .c: 13 | $(CC) $(CFLAGS) $< -o $@ $(LIBS) 14 | 15 | clean: 16 | rm -rf bin/* 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | m4/* 2 | *.[ao] 3 | *.exe 4 | *.lo 5 | *.la 6 | .libs 7 | libtool 8 | ltmain.sh 9 | .deps 10 | INSTALL 11 | main 12 | Makefile.in 13 | config.guess 14 | config.h 15 | config.h.in 16 | config.log 17 | config.status 18 | config.sub 19 | aclocal.m4 20 | compile 21 | autom4te.cache 22 | configure 23 | depcomp 24 | install-sh 25 | vector 26 | missing 27 | stamp-h1 28 | *~ 29 | libobject-*.tar.gz 30 | .*.swp 31 | *.log 32 | *.trs 33 | libobjectconfig.h 34 | test-driver 35 | como 36 | *.so 37 | -------------------------------------------------------------------------------- /test/slot: -------------------------------------------------------------------------------- 1 | function init() { 2 | name = []; 3 | 4 | print(name["DFDFDF"]); 5 | 6 | 7 | array = [1,2,3,4]; 8 | 9 | for(i = 0; i < len(array); i++) { 10 | print( array [ i ] ); 11 | } 12 | } 13 | 14 | function init_proxy() { 15 | init(); 16 | } 17 | 18 | 19 | 20 | function noop() { 21 | 22 | } 23 | 24 | noop(); 25 | 26 | 27 | /* 28 | * Should die as so (last call first) 29 | * init 30 | * - > init_proxy 31 | * - > __main_:x 32 | */ 33 | init_proxy(); 34 | -------------------------------------------------------------------------------- /vm/Makefile: -------------------------------------------------------------------------------- 1 | OS_TYPE := $(shell sh -c 'uname -s') 2 | COMO_OBJECT_LIB=-lcomoobject 3 | COMO_OBJECT_LIB_LD_FLAGS= -shared 4 | BINDIR = $(HOME)/objlib/bin 5 | CFLAGS = -rdynamic -O0 -Wall -Wextra -g -ggdb -std=c99 -I$(HOME)/objlib/src -L$(BINDIR) -Wl,-rpath=$(BINDIR) 6 | LIBS := $(COMO_OBJECT_LIB) 7 | SOURCES = $(shell echo *.c) 8 | COMO_OBJECT_LIB_OBJECTS =vm3 9 | 10 | all: $(COMO_OBJECT_LIB_OBJECTS) 11 | 12 | .c: 13 | $(CC) como_debug.c $(CFLAGS) $< -o $@ $(LIBS) 14 | 15 | clean: 16 | rm -rf vm3 17 | -------------------------------------------------------------------------------- /test/array2: -------------------------------------------------------------------------------- 1 | 2 | NAME_55555 = []; 3 | 4 | 5 | for(i = 0; i < 5; i++) { 6 | value = NAME_55555[ i ]; 7 | 8 | /* You'd think this would work, but what is happening 9 | is typeoif takes the type of NAME_55555 and then the subscript operator 10 | gets the value at index i of the value returned by typeof 11 | 12 | typeof NAME_55555 == "array"; 13 | "array"[i] 14 | @TODO refactor typeof grammar to allow for a primary expression on the rightt 15 | */ 16 | print( typeof NAME_55555 [ i ] ); 17 | } 18 | -------------------------------------------------------------------------------- /test/test_como_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../como_io.h" 5 | 6 | int main(void) 7 | { 8 | char *contents = como_read_file("test_como_io.c"); 9 | 10 | if(contents == NULL) { 11 | printf("ERROR READING"); 12 | } 13 | 14 | printf("%s", contents); 15 | 16 | free(contents); 17 | 18 | 19 | contents = como_read_file("THIS_DOES_NOT_EXIST"); 20 | if(contents == NULL) { 21 | printf("success\n"); 22 | } 23 | 24 | 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /vm/builtin_readline.c: -------------------------------------------------------------------------------- 1 | #define COMO_BUILTIN_READLINE_BEGIN 2 | COMO_UNUSED(args); 3 | 4 | char *buffer = malloc(8); 5 | size_t buffersize = 8; 6 | size_t i = 0; 7 | int c; 8 | como_object *retval; 9 | 10 | while((c = getchar()) != EOF) { 11 | if(i == buffersize) { 12 | buffer = realloc(buffer, buffersize * 2); 13 | buffersize = buffersize * 2; 14 | } 15 | 16 | if(c == '\n') 17 | break; 18 | 19 | buffer[i++] = c; 20 | } 21 | 22 | buffer[i] = '\0'; 23 | 24 | retval = como_stringfromstring(buffer); 25 | 26 | free(buffer); 27 | 28 | return retval; 29 | #define COMO_BUILTIN_READLINE_END 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -DCOMO_DEBUG=1 -L/usr/local/lib -g -Wall -Wextra -Wunused-parameter 2 | LIBS = -lobject 3 | YACC = bison 4 | YFLAGS = -y --warnings=all 5 | LEX = flex 6 | 7 | como: como_object.o como_ast_free.o como_io.o como_executor.o como_ast.o \ 8 | como_debug.o como_stack.o como_lexer.o como_parser.o como_compiler.o vm.o \ 9 | como_opcode.o como.o 10 | $(CC) como_io.o como_object.o como_executor.o como_debug.o como_ast.o \ 11 | como_stack.o como_parser.o como_lexer.o como_compiler.o vm.o \ 12 | como_ast_free.o como_opcode.o como.o -o como $(CFLAGS) $(LIBS) 13 | 14 | como_lexer.o: como_parser.c como_lexer.l 15 | 16 | clean: 17 | rm -f *.o como_lexer.c como_lexer.h como_parser.c como_parser.h como 18 | 19 | -------------------------------------------------------------------------------- /README.STYLE_GUIDE: -------------------------------------------------------------------------------- 1 | - Whitespace is to 4 spaces (not tabs) 2 | - Blocks are to be on a new line 3 | BAD: 4 | for(;;) {} 5 | GOOD: 6 | for(;;) 7 | { 8 | } 9 | - The pointer should be defined with the * on the name, not the type 10 | BAD: 11 | Frame* frame; 12 | GOOD: 13 | Frame *frame; 14 | 15 | - structs should have all their members visually alligned 16 | - Lines must not exceed 80 chars, truncate the line at 80 chars, and hen indent 17 | twice (8 spaces) 18 | 19 | - Naming: 20 | - try to prefix file names with como_*( 21 | - use a noun instead of a verb for the second part of the name 22 | - $PREFIX_NOUN 23 | - como_executor instead of como_execute 24 | 25 | -------------------------------------------------------------------------------- /como_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | #ifndef COMO_IO_H 18 | #define COMO_IO_H 19 | 20 | char *como_read_file(const char *filename); 21 | 22 | #endif /* !COMO_IO_H */ 23 | -------------------------------------------------------------------------------- /test/recursiveprint: -------------------------------------------------------------------------------- 1 | function recursive_print(value, indent) { 2 | if(typeof value == "array") { 3 | for(x = 0; x < indent; x++) { 4 | write(" "); 5 | } 6 | print("["); 7 | for(i = 0; i < len(value); i++) { 8 | recursive_print(value[i], indent + 1); 9 | } 10 | for(x = 0; x < indent; x++) { 11 | write(" "); 12 | } 13 | print("]"); 14 | } else { 15 | for(x = 0; x < indent; x++) { 16 | write(" "); 17 | } 18 | if(typeof value == "string") { 19 | write("'"); 20 | write(value); 21 | write("'"); 22 | } else { 23 | write(value); 24 | } 25 | print(""); 26 | } 27 | } 28 | 29 | function pretty_print(object) { 30 | recursive_print(object, 0); 31 | } 32 | 33 | names = [ 34 | "Bar", 35 | [ 36 | 10, 20, 30, 40, 50, 37 | [ 38 | "Hello", 39 | "World" 40 | ] 41 | ] 42 | ]; 43 | 44 | pretty_print(names); 45 | -------------------------------------------------------------------------------- /como_opcode.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "como_opcode.h" 5 | #include "como_debug.h" 6 | #include "como_compiler.h" 7 | 8 | const char * const str_opcodelist[] = { 9 | "INONE", "LOAD_CONST", "STORE_NAME", "LOAD_NAME", "IS_LESS_THAN", 10 | "JZ", "IPRINT", "IADD", "JMP", "IRETURN", "NOP", "LABEL", "HALT" 11 | }; 12 | 13 | static char opcodebuffer[1024]; 14 | 15 | const char *instrstr(ComoOpCode *op) 16 | { 17 | memset(opcodebuffer, 0, sizeof(opcodebuffer)); 18 | 19 | const char *opcode = str_opcodelist[op->op_code]; 20 | 21 | memcpy(opcodebuffer, opcode, strlen(opcode)); 22 | memcpy(opcodebuffer + strlen(opcode), " ", 1); 23 | 24 | if(op->operand != NULL) 25 | { 26 | char *operand = objectToString(op->operand); 27 | 28 | memcpy(opcodebuffer + strlen(opcode) + 1, operand, 29 | strlen(operand) + 1); 30 | 31 | free(operand); 32 | } 33 | 34 | return opcodebuffer; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /como_stack.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful , 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_STACK_H 19 | #define COMO_STACK_H 20 | 21 | typedef struct como_stack 22 | { 23 | void *value; 24 | struct como_stack *next; 25 | } como_stack; 26 | 27 | extern int como_stack_push(como_stack **stack, void *value); 28 | 29 | #endif /* !COMO_STACK_H */ 30 | -------------------------------------------------------------------------------- /vm/lang/graph.h: -------------------------------------------------------------------------------- 1 | #ifndef GRAPH_H 2 | #define GRAPH_H 3 | 4 | #include 5 | 6 | typedef enum { 7 | COMO_AST_INT, 8 | COMO_AST_STRING, 9 | COMO_AST_DOUBLE, 10 | COMO_AST_ID, 11 | COMO_AST_BINOP, 12 | COMO_AST_TOP /* Represents the root of the graph */ 13 | } node_kind; 14 | 15 | typedef uint16_t node_attributes; 16 | typedef unsigned int node_line; 17 | 18 | typedef struct _node { 19 | node_kind kind; 20 | node_attributes attributes; 21 | node_line line; 22 | const char *info; 23 | int nchild; 24 | struct _node **children; 25 | void(*attribute_visit)(struct _node *); 26 | void(*visit)(struct _node *, int); 27 | } node; 28 | 29 | /* edge nodes */ 30 | typedef struct _node_int { 31 | node base; 32 | int value; 33 | } node_int; 34 | 35 | typedef struct _node_double { 36 | node base; 37 | double value; 38 | } node_double; 39 | 40 | typedef struct _node_string { 41 | node base; 42 | char *value; 43 | } node_string; 44 | 45 | typedef struct _node_id { 46 | node base; 47 | char *value; 48 | } node_id; 49 | 50 | 51 | #endif -------------------------------------------------------------------------------- /como_globals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful , 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_GLOBALS_H 19 | #define COMO_GLOBALS_H 20 | 21 | #include 22 | 23 | #include "como_debug.h" 24 | #include "como_opcode.h" 25 | 26 | #define COMO_FAILURE 1 27 | #define COMO_SUCCESS 0 28 | 29 | extern char *get_active_file_name(void); 30 | 31 | #endif /* !COMO_GLOBALS_H */ 32 | -------------------------------------------------------------------------------- /como_executor.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_EXECUTE_H 19 | #define COMO_EXECUTE_H 20 | 21 | #include 22 | 23 | #include "como_opcode.h" 24 | 25 | typedef void(*como_opcode_handler)(Object *arg1, Object *arg2, 26 | Object **retval); 27 | 28 | extern const como_opcode_handler como_opcode_handler_table[COMO_OPCODE_MAX]; 29 | 30 | #endif /* !COMO_EXECUTE_H */ 31 | -------------------------------------------------------------------------------- /como_stack.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | 20 | #include "como_stack.h" 21 | 22 | int como_stack_push(como_stack **stack, void *value) 23 | { 24 | if(*stack == NULL) 25 | { 26 | como_stack* _list = malloc(sizeof(como_stack)); 27 | _list->value = value; 28 | _list->next = NULL; 29 | *stack = _list; 30 | } 31 | else 32 | { 33 | como_stack* prev = (*stack); 34 | como_stack* next = malloc(sizeof(como_stack)); 35 | next->value = value; 36 | next->next = prev; 37 | (*stack) = next; 38 | } 39 | 40 | return 1; 41 | } -------------------------------------------------------------------------------- /vm/program.c: -------------------------------------------------------------------------------- 1 | /* sum = 15 + 5 */ 2 | emit(frame, LOAD_CONST, int_const(frame, 15), 0); 3 | emit(frame, LOAD_CONST, int_const(frame, 5), 0); 4 | emit(frame, IADD, 0, 0); 5 | emit(frame, STORE_NAME, str_const(frame, "sum"), 0); 6 | 7 | /* print sumcopy = sum */ 8 | emit(frame, LOAD_NAME, str_const(frame, "sum"), 0); 9 | emit(frame, STORE_NAME, str_const(frame, "sumcopy"), 0); 10 | emit(frame, IPRINT, 0, 0); 11 | 12 | 13 | /* result = 5 * 5; */ 14 | emit(frame, LOAD_CONST, int_const(frame, 5), 0); 15 | emit(frame, LOAD_CONST, int_const(frame, 5), 0); 16 | emit(frame, ITIMES, 0, 0); 17 | emit(frame, STORE_NAME, str_const(frame, "result"), 0); 18 | 19 | /* print sum == result */ 20 | emit(frame, LOAD_NAME, str_const(frame, "sum"), 0); 21 | emit(frame, LOAD_NAME, str_const(frame, "result"), 0); 22 | emit(frame, EQUAL, 0, 0); 23 | emit(frame, IPRINT, 0, 0); 24 | 25 | 26 | /* add(26, 1); */ 27 | emit(frame, LOAD_CONST, int_const(frame, 26), 0); 28 | emit(frame, LOAD_CONST, int_const(frame, 1), 0); 29 | emit(frame, LOAD_NAME, str_const(frame, "add"), 0); 30 | emit(frame, CALL, 2, 0); 31 | 32 | 33 | /* print "returning " + "from main"; */ 34 | emit(frame, LOAD_CONST, str_const(frame, "returning "), 0); 35 | emit(frame, LOAD_CONST, str_const(frame, "from main"), 0); 36 | emit(frame, IADD, 0, 0); 37 | emit(frame, IPRINT, 0, 0); 38 | 39 | 40 | 41 | 42 | /* return 1; */ 43 | emit(frame, LOAD_CONST, int_const(frame, 0), 0); 44 | emit(frame, IRETURN, 0, 1); -------------------------------------------------------------------------------- /como_object.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_OBJECT_UTIL 19 | #define COMO_OBJECT_UTIL 20 | 21 | #include 22 | #include 23 | 24 | typedef struct ComoBuiltinFunction 25 | { 26 | Object *(*handler)(Object *); 27 | } ComoBuiltinFunction; 28 | 29 | #define Obj_CheckExact(Obj, Type) \ 30 | (O_TYPE((Obj)) == (Type)) 31 | 32 | #define ComoString_Check(obj) O_TYPE((obj)) == IS_STRING ? 1 : 0 33 | 34 | #define COMO__ARRAY_FOREACH(_ar) do { \ 35 | size_t _i; \ 36 | for(_i = 0; _i < O_AVAL((_ar))->size; _i++) { \ 37 | Object* _value = O_AVAL((_ar))->table[_i]; \ 38 | 39 | #define COMO_ARRAY_FOREACH(_ar, _val) \ 40 | COMO__ARRAY_FOREACH(_ar) \ 41 | _val = _value; \ 42 | 43 | #define COMO_ARRAY_FOREACH_END() \ 44 | } \ 45 | } while(0) 46 | 47 | void como_printf(const char *format, ...); 48 | 49 | int como_object_is_truthy(Object *obj); 50 | 51 | #endif -------------------------------------------------------------------------------- /como_debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "como_debug.h" 24 | #include "como_compiler.h" 25 | 26 | void como_debug_ex(const char *f, const char *fn, int ln, 27 | const char* format, ...) 28 | { 29 | fprintf(stderr, ANSI_COLOR_GREEN "debug: %s:%s:%d: ", f, fn, ln); 30 | va_list args; 31 | va_start (args, format); 32 | vfprintf (stderr, format, args); 33 | va_end (args); 34 | fprintf(stderr, ANSI_COLOR_RESET "\n"); 35 | fflush(stderr); 36 | } 37 | 38 | void __attribute__ ((noreturn)) como_error_noreturn_ex(ComoFrame *frame, const char *f, 39 | const char *fn, int ln, const char* format, ...) 40 | { 41 | fprintf(stderr, ANSI_COLOR_RED "fatal: %s:%s:%d: ", f, fn, ln); 42 | va_list args; 43 | va_start (args, format); 44 | vfprintf (stderr, format, args); 45 | va_end (args); 46 | fprintf(stderr, ANSI_COLOR_RESET "\n"); 47 | 48 | if(frame != NULL) { 49 | como_print_stack_trace(frame); 50 | } 51 | 52 | fflush(stderr); 53 | exit(1); 54 | } 55 | -------------------------------------------------------------------------------- /como.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "como_ast.h" 24 | #include "como_stack.h" 25 | #include "como_compiler.h" 26 | #include "como_debug.h" 27 | #include "como_opcode.h" 28 | #include "como_globals.h" 29 | #include "como_parser.h" 30 | #include "como_lexer.h" 31 | 32 | int yyparse(ast_node **ast, yyscan_t scanner); 33 | 34 | int main(int argc, char **argv) 35 | { 36 | ast_node *program; 37 | 38 | if(argc < 2) 39 | { 40 | fputs( 41 | "Usage: como FLAGS FILE | como FILE\n" 42 | "FLAGS\n" 43 | " --print-asm print instructions generated by the compiler\n", 44 | stdout); 45 | 46 | return 0; 47 | } 48 | 49 | 50 | if(argc == 3 && strcmp(argv[1], "--print-asm") == 0) 51 | { 52 | program = como_parse(argv[2]); 53 | como_dump_asm(program, argv[2]); 54 | } 55 | else 56 | { 57 | program = como_parse(argv[1]); 58 | como_compile_ast(program, argv[1]); 59 | } 60 | 61 | ast_node_free(program); 62 | 63 | return 0; 64 | } 65 | -------------------------------------------------------------------------------- /como_compiler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_COMPILER_H 19 | #define COMO_COMPILER_H 20 | 21 | #include 22 | #include 23 | 24 | #include "como_ast.h" 25 | #include "como_stack.h" 26 | 27 | #define COMO_DEFAULT_FRAME_STACKSIZE 2048U 28 | 29 | #define COMO_OP_CODE_OPERAND_USED 0x0001 30 | #define COMO_OP_CODE_OPERAND_FREE 0x0002 31 | 32 | typedef struct ComoFrame 33 | { 34 | long cf_sp; 35 | size_t cf_stack_size; 36 | Object *cf_stack[(size_t)COMO_DEFAULT_FRAME_STACKSIZE]; 37 | Object *cf_symtab; 38 | Object *code; 39 | long pc; 40 | struct ComoFrame *next; 41 | Object *namedparameters; 42 | Object *filename; 43 | como_stack *call_stack; 44 | Object *name; 45 | Object *lineno; 46 | struct ComoFrame *caller; 47 | } ComoFrame; 48 | 49 | void como_print_stack_trace(ComoFrame *frame); 50 | void como_compile_ast(ast_node *p, const char *filename); 51 | void como_dump_asm(ast_node *p, const char *filename); 52 | void como_compile(ast_node* p, ComoFrame *frame); 53 | 54 | #endif /* !COMO_COMPILER_H */ 55 | -------------------------------------------------------------------------------- /como_debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_DEBUG_H 19 | #define COMO_DEBUG_H 20 | 21 | #include 22 | 23 | #include "como_compiler.h" 24 | 25 | #ifdef COMO_DEBUG 26 | #define COMO_HAVE_DEBUG 1 27 | #endif 28 | 29 | #define ANSI_COLOR_RED "\x1b[31m" 30 | #define ANSI_COLOR_GREEN "\x1b[32m" 31 | #define ANSI_COLOR_YELLOW "\x1b[33m" 32 | #define ANSI_COLOR_BLUE "\x1b[34m" 33 | #define ANSI_COLOR_MAGENTA "\x1b[35m" 34 | #define ANSI_COLOR_CYAN "\x1b[36m" 35 | #define ANSI_COLOR_RESET "\x1b[0m" 36 | 37 | void como_debug_ex(const char *f, 38 | const char *fn, int ln, const char* format, ...); 39 | 40 | void __attribute__ ((noreturn)) como_error_noreturn_ex(ComoFrame *frame, const char *f, 41 | const char *fn, int ln, const char* format, ...); 42 | 43 | #define como_error_noreturn(frame, format, ...) \ 44 | como_error_noreturn_ex(frame, __FILE__, __func__, __LINE__, format, ##__VA_ARGS__) 45 | 46 | #ifdef COMO_HAVE_DEBUG 47 | #define como_debug(format, ...) \ 48 | como_debug_ex(__FILE__, __func__, __LINE__, format, ##__VA_ARGS__) 49 | 50 | #else 51 | #define como_debug(format, ...) 52 | #endif 53 | 54 | #define COMO_OOM() do { \ 55 | como_error_noreturn("out of memory"); \ 56 | } while(0) 57 | 58 | #endif /* !COMO_DEBUG_H */ 59 | 60 | -------------------------------------------------------------------------------- /vm/lang/lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | #include 4 | #include 5 | #include 6 | #include "como_parser.h" 7 | 8 | %} 9 | 10 | %option outfile="como_lexer.c" header-file="como_lexer.h" 11 | %option warn nodefault 12 | %option reentrant noyywrap never-interactive nounistd yylineno 13 | %option bison-bridge bison-locations 14 | %option nounput noinput 15 | 16 | WHITE_SPACE [ \r\n\t]* 17 | L [a-zA-Z_] 18 | A [a-zA-Z_0-9] 19 | D [0-9] 20 | 21 | 22 | %% 23 | 24 | "function" { return T_FUNCTION; } 25 | "class" { return T_CLASS; } 26 | "public" { return T_PUBLIC; } 27 | "private" { return T_PRIVATE; } 28 | "protected" { return T_PROTECTED; } 29 | "import" { return T_IMPORT; } 30 | "new" { return T_NEW; } 31 | 32 | {WHITE_SPACE} { /* Skipping Blanks Today */ } 33 | {L}{A}* { 34 | size_t len = strlen(yytext); 35 | yylval->id = malloc(len + 1); 36 | memcpy(yylval->id, yytext, len + 1); 37 | return T_ID; 38 | } 39 | 40 | {D}+ { 41 | yylval->lval = strtol(yytext, NULL, 10); 42 | return T_INT; 43 | } 44 | 45 | {D}+"."{D}+ { 46 | yylval->dval = atof(yytext); 47 | return T_DOUBLE; 48 | } 49 | 50 | 51 | L?\"(\\.|[^\\"])*\" { 52 | size_t len = strlen(yytext); 53 | if(len > 2U) { 54 | yylval->stringliteral = malloc((len -2) + 1); 55 | memcpy(yylval->stringliteral, yytext + 1, len - 2); 56 | yylval->stringliteral[len - 2] = '\0'; 57 | return T_STR_LIT; 58 | } else { 59 | yylval->stringliteral = malloc(1); 60 | yylval->stringliteral[0] = '\0'; 61 | return T_STR_LIT; 62 | } 63 | } 64 | 65 | L?\'(\\.|[^\\'])*\' { 66 | size_t len = strlen(yytext); 67 | if(len > 2U) { 68 | yylval->stringliteral = malloc((len -2) + 1); 69 | memcpy(yylval->stringliteral, yytext + 1, len - 2); 70 | yylval->stringliteral[len - 2] = '\0'; 71 | return T_STR_LIT; 72 | } else { 73 | yylval->stringliteral = malloc(1); 74 | yylval->stringliteral[0] = '\0'; 75 | return T_STR_LIT; 76 | } 77 | } 78 | 79 | . { return yytext[0]; } 80 | 81 | %% 82 | -------------------------------------------------------------------------------- /vm/lang/io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | 25 | /** 26 | * POSIX compliant 27 | */ 28 | char *como_read_file(const char *path) 29 | { 30 | int fd; 31 | off_t length; 32 | char *buffer; 33 | ssize_t nread; 34 | struct stat stat_buffer; 35 | 36 | fd = open(path, O_RDONLY); 37 | 38 | if(fd == -1) 39 | { 40 | return NULL; 41 | } 42 | 43 | if((fstat(fd, &stat_buffer) != 0)) 44 | { 45 | close(fd); 46 | return NULL; 47 | } 48 | 49 | if(!S_ISREG(stat_buffer.st_mode)) 50 | { 51 | close(fd); 52 | return NULL; 53 | } 54 | 55 | length = stat_buffer.st_size; 56 | 57 | if(length + 1 < length) 58 | { 59 | close(fd); 60 | return NULL; 61 | } 62 | 63 | buffer = malloc(length + 1); 64 | 65 | if(buffer == NULL) 66 | { 67 | close(fd); 68 | free(buffer); 69 | return NULL; 70 | } 71 | 72 | nread = read(fd, buffer, (size_t)length); 73 | 74 | if(nread != length) 75 | { 76 | close(fd); 77 | free(buffer); 78 | return NULL; 79 | } 80 | 81 | close(fd); 82 | 83 | buffer[length] = '\0'; 84 | 85 | return buffer; 86 | } 87 | -------------------------------------------------------------------------------- /como_io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "como_io.h" 25 | 26 | /** 27 | * POSIX compliant 28 | */ 29 | char *como_read_file(const char *path) 30 | { 31 | int fd; 32 | off_t length; 33 | char *buffer; 34 | ssize_t nread; 35 | struct stat stat_buffer; 36 | 37 | fd = open(path, O_RDONLY); 38 | 39 | if(fd == -1) 40 | { 41 | return NULL; 42 | } 43 | 44 | if((fstat(fd, &stat_buffer) != 0)) 45 | { 46 | close(fd); 47 | return NULL; 48 | } 49 | 50 | if(!S_ISREG(stat_buffer.st_mode)) 51 | { 52 | close(fd); 53 | return NULL; 54 | } 55 | 56 | length = stat_buffer.st_size; 57 | 58 | if(length + 1 < length) 59 | { 60 | close(fd); 61 | return NULL; 62 | } 63 | 64 | buffer = malloc(length + 1); 65 | 66 | if(buffer == NULL) 67 | { 68 | close(fd); 69 | free(buffer); 70 | return NULL; 71 | } 72 | 73 | nread = read(fd, buffer, (size_t)length); 74 | 75 | if(nread != length) 76 | { 77 | close(fd); 78 | free(buffer); 79 | return NULL; 80 | } 81 | 82 | close(fd); 83 | 84 | buffer[length] = '\0'; 85 | 86 | return buffer; 87 | } 88 | -------------------------------------------------------------------------------- /como_object.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include "como_object.h" 23 | 24 | void como_printf(const char *format, ...) { 25 | va_list args; 26 | va_start (args, format); 27 | while(*format) { 28 | if(*format == ':') { 29 | Object *arg; 30 | format++; 31 | switch(*format) { 32 | case 'S': 33 | arg = va_arg(args, Object *); 34 | fprintf(stdout, "%s", O_SVAL(arg)->value); 35 | break; 36 | case 'L': 37 | arg = va_arg(args, Object *); 38 | fprintf(stdout, "%ld", O_LVAL(arg)); 39 | break; 40 | case 'F': 41 | arg = va_arg(args, Object *); 42 | fprintf(stdout, "%.*G", DBL_DIG, O_DVAL(arg)); 43 | break; 44 | default: 45 | format--; 46 | fputc(*format, stdout); 47 | break; 48 | } 49 | } else { 50 | fputc(*format, stdout); 51 | } 52 | format++; 53 | } 54 | va_end (args); 55 | } 56 | 57 | int como_object_is_truthy(Object *obj) 58 | { 59 | /* C truthiness */ 60 | if(O_TYPE(obj) == IS_LONG && O_LVAL(obj) == 0L) 61 | { 62 | return 0; 63 | } 64 | else 65 | { 66 | return 1; 67 | } 68 | } -------------------------------------------------------------------------------- /vm/como_debug.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_DEBUG_H 19 | #define COMO_DEBUG_H 20 | 21 | #include 22 | 23 | #ifdef COMO_DEBUG 24 | #define COMO_HAVE_DEBUG 1 25 | #endif 26 | 27 | #define ANSI_COLOR_RED "\x1b[31m" 28 | #define ANSI_COLOR_GREEN "\x1b[32m" 29 | #define ANSI_COLOR_YELLOW "\x1b[33m" 30 | #define ANSI_COLOR_BLUE "\x1b[34m" 31 | #define ANSI_COLOR_MAGENTA "\x1b[35m" 32 | #define ANSI_COLOR_CYAN "\x1b[36m" 33 | #define ANSI_COLOR_RESET "\x1b[0m" 34 | 35 | 36 | void __attribute__ ((noreturn)) como_error_noreturn_ex(const char *f, 37 | const char *fn, int ln, const char* format, ...); 38 | 39 | #define como_error_noreturn(format, ...) \ 40 | como_error_noreturn_ex(__FILE__, __func__, __LINE__, format, ##__VA_ARGS__) 41 | 42 | #ifdef COMO_HAVE_DEBUG 43 | #define como_debug(format, ...) \ 44 | como_debug_ex(__FILE__, __func__, __LINE__, format, ##__VA_ARGS__) 45 | 46 | void como_debug_ex(const char *f, 47 | const char *fn, int ln, const char* format, ...); 48 | 49 | #else 50 | #define como_debug(format, ...) 51 | #endif 52 | 53 | #ifdef COMO_WARNING 54 | #define como_warning(format, ...) \ 55 | como_warning_ex(__FILE__, __func__, __LINE__, format, ##__VA_ARGS__) 56 | 57 | void como_warning_ex(const char *f, 58 | const char *fn, int ln, const char* format, ...); 59 | 60 | #else 61 | #define como_warning(format, ...) 62 | #endif 63 | 64 | 65 | #define COMO_OOM() do { \ 66 | como_error_noreturn("out of memory"); \ 67 | } while(0) 68 | 69 | #endif /* !COMO_DEBUG_H */ 70 | 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # COMO: como object meta oriented 2 | 3 | # even.como 4 | ``` 5 | func isodd(num) { 6 | i = 0; 7 | odd = 0; 8 | while((i == num) == 0) { 9 | if(odd == 0) { 10 | odd = 1; 11 | } else { 12 | odd = 0; 13 | } 14 | i = i + 1; 15 | } 16 | return odd; 17 | } 18 | 19 | func print_even(num) 20 | { 21 | while(num) { 22 | if(isodd(num)) { 23 | print(num + " is odd"); 24 | } else { 25 | print(num + " is even"); 26 | } 27 | num = num - 1; 28 | } 29 | } 30 | 31 | print_even(4); 32 | ``` 33 | 34 | # range 35 | ``` 36 | func print_range(a, b) { 37 | while(a <= b) { 38 | print(a); 39 | a = a + 1; 40 | } 41 | } 42 | ``` 43 | 44 | # Build Requirements 45 | I only compile this on UNIX like systems. You need the standard build tools 46 | such as `autoconf`, `libtool`, and `flex` and `bison`. Also there is a runtime 47 | requirement : `libobject` which you can get at https://github.com/libobject/libobject 48 | 49 | # License 50 | Please see the file LICENSE located in the root directory of the project. 51 | 52 | 53 | # Virtual Machine 54 | The compiler generates instructions of the custom instruction set (como_opcode.h) The virtual machine is stack based and only a few instructions take operands. Most instructions pop there arguments from the stack. Here is the relevant source code and instruction output generated from the compiler. This was run with the following command: `./como --print-asm test/for_loop` 55 | 56 | ``` 57 | for(i = 5; i >= 0; i--) 58 | { 59 | if(i % 2 == 0) { 60 | print(i); 61 | } 62 | } 63 | ``` 64 | 65 | ``` 66 | 0 LABEL 67 | 1 LOAD_CONST 5 68 | 2 STORE_NAME i 69 | 3 LOAD_NAME i 70 | 4 LOAD_CONST 0 71 | 5 IS_GREATER_THAN_OR_EQUAL 72 | 6 JZ 25 73 | 7 LABEL 74 | 8 LOAD_NAME i 75 | 9 LOAD_CONST 2 76 | 10 IREM 77 | 11 LOAD_CONST 0 78 | 12 IS_EQUAL 79 | 13 JZ 17 80 | 14 LOAD_NAME i 81 | 15 IPRINT 82 | 16 JMP 18 83 | 17 LABEL 84 | 18 LABEL 85 | 19 POSTFIX_DEC 86 | 20 LOAD_NAME i 87 | 21 LOAD_CONST 0 88 | 22 IS_GREATER_THAN_OR_EQUAL 89 | 23 JZ 25 90 | 24 JMP 7 91 | 25 LABEL 92 | 26 HALT 93 | ``` 94 | 95 | # Problems 96 | - There is no `continue`, or `break` statements in loops at this time. The first idea that comes to mind to implement this is to have the executor keep track if the VM is in a loop and store the correct JMP address. This is on the roadmap for 2017 -------------------------------------------------------------------------------- /vm/como_debug.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "como_debug.h" 24 | 25 | void como_warning_ex(const char *f, const char *fn, int ln, 26 | const char* format, ...) 27 | { 28 | if(isatty(2)) 29 | fprintf(stderr, ANSI_COLOR_YELLOW "warning: %s:%s:%d: ", f, fn, ln); 30 | else 31 | fprintf(stderr, "warning: %s:%s:%d: ", f, fn, ln); 32 | 33 | va_list args; 34 | va_start (args, format); 35 | vfprintf (stderr, format, args); 36 | va_end (args); 37 | 38 | if(isatty(2)) 39 | fprintf(stderr, ANSI_COLOR_RESET "\n"); 40 | else 41 | fprintf(stderr, "\n"); 42 | 43 | fflush(stderr); 44 | } 45 | 46 | void como_debug_ex(const char *f, const char *fn, int ln, 47 | const char* format, ...) 48 | { 49 | if(isatty(2)) 50 | fprintf(stderr, ANSI_COLOR_BLUE "debug: %s:%s:%d: ", f, fn, ln); 51 | else 52 | fprintf(stderr, ANSI_COLOR_BLUE "debug: %s:%s:%d: ", f, fn, ln); 53 | 54 | va_list args; 55 | va_start (args, format); 56 | vfprintf (stderr, format, args); 57 | va_end (args); 58 | 59 | if(isatty(2)) 60 | fprintf(stderr, ANSI_COLOR_RESET "\n"); 61 | else 62 | fprintf(stderr, "\n"); 63 | 64 | fflush(stderr); 65 | } 66 | 67 | void __attribute__ ((noreturn)) como_error_noreturn_ex(const char *f, 68 | 69 | const char *fn, int ln, const char* format, ...) 70 | { 71 | fprintf(stderr, ANSI_COLOR_RED "fatal: %s:%s:%d: ", f, fn, ln); 72 | va_list args; 73 | va_start (args, format); 74 | vfprintf (stderr, format, args); 75 | va_end (args); 76 | fprintf(stderr, ANSI_COLOR_RESET "\n"); 77 | 78 | fflush(stderr); 79 | exit(1); 80 | } 81 | -------------------------------------------------------------------------------- /support/.vimrc: -------------------------------------------------------------------------------- 1 | " All system-wide defaults are set in $VIMRUNTIME/debian.vim (usually just 2 | " /usr/share/vim/vimcurrent/debian.vim) and sourced by the call to :runtime 3 | " you can find below. If you wish to change any of those settings, you should 4 | " do it in this file (/etc/vim/vimrc), since debian.vim will be overwritten 5 | " everytime an upgrade of the vim packages is performed. It is recommended to 6 | " make changes after sourcing debian.vim since it alters the value of the 7 | " 'compatible' option. 8 | 9 | " This line should not be removed as it ensures that various options are 10 | " properly set to work with the Vim-related packages available in Debian. 11 | runtime! debian.vim 12 | 13 | " Uncomment the next line to make Vim more Vi-compatible 14 | " NOTE: debian.vim sets 'nocompatible'. Setting 'compatible' changes numerous 15 | " options, so any other options should be set AFTER setting 'compatible'. 16 | "set compatible 17 | 18 | " Vim5 and later versions support syntax highlighting. Uncommenting the next 19 | " line enables syntax highlighting by default. 20 | if has("syntax")" 21 | syntax on 22 | endif 23 | 24 | " If using a dark background within the editing area and syntax highlighting 25 | " turn on this option as well 26 | set background=light 27 | 28 | " Uncomment the following to have Vim jump to the last position when 29 | " reopening a file 30 | if has("autocmd") 31 | au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif 32 | endif 33 | 34 | " Uncomment the following to have Vim load indentation rules and plugins 35 | " according to the detected filetype. 36 | if has("autocmd") 37 | filetype plugin indent on 38 | endif 39 | 40 | " The following are commented out as they cause vim to behave a lot 41 | " differently from regular Vi. They are highly recommended though. 42 | set showcmd " Show (partial) command in status line. 43 | set showmatch " Show matching brackets. 44 | set ignorecase " Do case insensitive matching 45 | set smartcase " Do smart case matching 46 | set incsearch " Incremental search 47 | set autowrite " Automatically save before commands like :next and :make 48 | set hidden " Hide buffers when they are abandoned 49 | set mouse=a " Enable mouse usage (all modes) 50 | set ruler 51 | set nu 52 | set backspace=indent,eol,start 53 | set tabstop=8 54 | set shiftwidth=4 55 | set softtabstop=0 56 | set expandtab 57 | set smarttab 58 | set nobackup 59 | set colorcolumn=80 60 | set encoding=utf-8 61 | cnoreabbrev X x 62 | cnoreabbrev W w 63 | 64 | " Source a global configuration file if available 65 | if filereadable("/etc/vim/vimrc.local") 66 | source /etc/vim/vimrc.local 67 | endif 68 | 69 | -------------------------------------------------------------------------------- /como_ast_free.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "como_ast.h" 6 | #include "como_globals.h" 7 | #include "como_debug.h" 8 | 9 | void ast_node_free(ast_node *p) 10 | { 11 | if(!p) 12 | return; 13 | switch(p->type) { 14 | case AST_NODE_TYPE_BOOL: 15 | /* Not implemented */ 16 | assert(0); 17 | break; 18 | case AST_NODE_TYPE_SLOT_ACCESS: 19 | ast_node_free(p->u1.slot_access_node.value); 20 | ast_node_free(p->u1.slot_access_node.index); 21 | free(p); 22 | break; 23 | case AST_NODE_TYPE_ARRAY: 24 | ast_node_free(p->u1.array_node.elements); 25 | free(p); 26 | break; 27 | case AST_NODE_TYPE_ASSERT: 28 | ast_node_free(p->u1.assert_node.expr); 29 | free(p); 30 | break; 31 | case AST_NODE_TYPE_TYPEOF: 32 | ast_node_free(p->u1.typeof_node.expr); 33 | free(p); 34 | break; 35 | case AST_NODE_TYPE_UNARY_OP: 36 | ast_node_free(p->u1.unary_node.expr); 37 | free(p); 38 | break; 39 | case AST_NODE_TYPE_POSTFIX: 40 | ast_node_free(p->u1.postfix_node.expr); 41 | free(p); 42 | break; 43 | case AST_NODE_TYPE_WHILE: 44 | ast_node_free(p->u1.while_node.condition); 45 | ast_node_free(p->u1.while_node.body); 46 | free(p); 47 | break; 48 | case AST_NODE_TYPE_FOR: 49 | ast_node_free(p->u1.for_node.initialization); 50 | ast_node_free(p->u1.for_node.condition); 51 | ast_node_free(p->u1.for_node.final_expression); 52 | ast_node_free(p->u1.for_node.body); 53 | free(p); 54 | break; 55 | case AST_NODE_TYPE_STRING: 56 | free(p->u1.string_value.value); 57 | free(p); 58 | break; 59 | case AST_NODE_TYPE_RET: 60 | ast_node_free(p->u1.return_node.expr); 61 | free(p); 62 | break; 63 | case AST_NODE_TYPE_IF: 64 | ast_node_free(p->u1.if_node.condition); 65 | ast_node_free(p->u1.if_node.b1); 66 | ast_node_free(p->u1.if_node.b2); 67 | free(p); 68 | break; 69 | case AST_NODE_TYPE_NUMBER: 70 | free(p); 71 | break; 72 | case AST_NODE_TYPE_STATEMENT_LIST: { 73 | size_t i; 74 | for(i = 0; i < p->u1.statements_node.count; i++) { 75 | ast_node_free(p->u1.statements_node.statement_list[i]); 76 | } 77 | free(p->u1.statements_node.statement_list); 78 | free(p); 79 | } 80 | break; 81 | case AST_NODE_TYPE_BIN_OP: 82 | ast_node_free(p->u1.binary_node.left); 83 | ast_node_free(p->u1.binary_node.right); 84 | free(p); 85 | break; 86 | case AST_NODE_TYPE_ID: 87 | free(p->u1.id_node.name); 88 | free(p); 89 | break; 90 | case AST_NODE_TYPE_FUNC_DECL: 91 | free(p->u1.function_node.name); 92 | ast_node_free(p->u1.function_node.parameter_list); 93 | ast_node_free(p->u1.function_node.body); 94 | free(p); 95 | break; 96 | case AST_NODE_TYPE_PRINT: 97 | ast_node_free(p->u1.print_node.expr); 98 | free(p); 99 | break; 100 | case AST_NODE_TYPE_CALL: 101 | ast_node_free(p->u1.call_node.id); 102 | ast_node_free(p->u1.call_node.arguments); 103 | free(p); 104 | break; 105 | default: 106 | como_error_noreturn(NULL, "p->type not implemented (%d)", p->type); 107 | break; 108 | } 109 | } -------------------------------------------------------------------------------- /vm/lang/como_parser.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.0.4. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . */ 19 | 20 | /* As a special exception, you may create a larger work that contains 21 | part or all of the Bison parser skeleton and distribute that work 22 | under terms of your choice, so long as that work isn't itself a 23 | parser generator using the skeleton or a modified version thereof 24 | as a parser skeleton. Alternatively, if you modify or redistribute 25 | the parser skeleton itself, you may (at your option) remove this 26 | special exception, which will cause the skeleton and the resulting 27 | Bison output files to be licensed under the GNU General Public 28 | License without this special exception. 29 | 30 | This special exception was added by the Free Software Foundation in 31 | version 2.2 of Bison. */ 32 | 33 | #ifndef YY_YY_COMO_PARSER_H_INCLUDED 34 | # define YY_YY_COMO_PARSER_H_INCLUDED 35 | /* Debug traces. */ 36 | #ifndef YYDEBUG 37 | # define YYDEBUG 0 38 | #endif 39 | #if YYDEBUG 40 | extern int yydebug; 41 | #endif 42 | /* "%code requires" blocks. */ 43 | #line 25 "grammar.y" /* yacc.c:1909 */ 44 | 45 | #include 46 | 47 | #ifndef YY_TYPEDEF_YY_SCANNER_T 48 | #define YY_TYPEDEF_YY_SCANNER_T 49 | typedef void* yyscan_t; 50 | #endif 51 | 52 | typedef struct ast_node { 53 | void *padding; 54 | } ast_node; 55 | 56 | 57 | #line 58 "como_parser.h" /* yacc.c:1909 */ 58 | 59 | /* Token type. */ 60 | #ifndef YYTOKENTYPE 61 | # define YYTOKENTYPE 62 | enum yytokentype 63 | { 64 | T_INT = 258, 65 | T_DOUBLE = 259, 66 | T_ID = 260, 67 | T_STR_LIT = 261, 68 | T_FUNCTION = 262, 69 | T_CLASS = 263, 70 | T_PUBLIC = 264, 71 | T_PROTECTED = 265, 72 | T_PRIVATE = 266, 73 | T_NEW = 267, 74 | T_IMPORT = 268 75 | }; 76 | #endif 77 | 78 | /* Value type. */ 79 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 80 | 81 | union YYSTYPE 82 | { 83 | #line 39 "grammar.y" /* yacc.c:1909 */ 84 | 85 | long lval; 86 | double dval; 87 | char* id; 88 | char* stringliteral; 89 | ast_node *ast; 90 | 91 | #line 92 "como_parser.h" /* yacc.c:1909 */ 92 | }; 93 | 94 | typedef union YYSTYPE YYSTYPE; 95 | # define YYSTYPE_IS_TRIVIAL 1 96 | # define YYSTYPE_IS_DECLARED 1 97 | #endif 98 | 99 | 100 | 101 | int yyparse (ast_node** ast, yyscan_t scanner); 102 | 103 | #endif /* !YY_YY_COMO_PARSER_H_INCLUDED */ 104 | -------------------------------------------------------------------------------- /como_opcode.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_OPCODE_H 19 | #define COMO_OPCODE_H 20 | 21 | #include 22 | 23 | typedef struct ComoOpCode 24 | { 25 | unsigned char op_code; 26 | unsigned int flags; 27 | unsigned int lineno; 28 | Object *operand; 29 | } ComoOpCode; 30 | 31 | #define INONE 0x00 32 | #define LOAD_CONST 0x01 33 | #define STORE_NAME 0x02 34 | #define LOAD_NAME 0x03 35 | #define IS_LESS_THAN 0x04 36 | #define JZ 0x05 37 | #define IPRINT 0x06 38 | #define IADD 0x07 39 | #define JMP 0x08 40 | #define IRETURN 0x09 41 | #define NOP 0x0a 42 | #define LABEL 0x0b 43 | #define HALT 0x0c 44 | #define IS_EQUAL 0x0d 45 | #define IDIV 0x0e 46 | #define ITIMES 0x0f 47 | #define IMINUS 0x10 48 | #define IS_GREATER_THAN 0x11 49 | #define IS_NOT_EQUAL 0x12 50 | #define IS_GREATER_THAN_OR_EQUAL 0x13 51 | #define IS_LESS_THAN_OR_EQUAL 0x14 52 | #define DEFINE_FUNCTION 0x15 53 | #define CALL_FUNCTION 0x16 54 | #define POSTFIX_INC 0x17 55 | #define UNARY_MINUS 0x18 56 | #define IREM 0x19 57 | #define POSTFIX_DEC 0x20 58 | #define ITYPEOF 0x21 59 | #define IASSERT 0x22 60 | #define UNARY_NOT 0x23 61 | #define IAND 0x24 62 | #define CREATE_ARRAY 0x25 63 | #define GET_FIELD 0x26 64 | #define UNARY_PLUS 0x27 65 | #define EQUAL 0x28 66 | #define NEQUAL 0x29 67 | #define GT 0x30 68 | #define LT 0x31 69 | #define GTE 0x32 70 | #define LTE 0x33 71 | #define TRY 0x34 72 | #define CATCH 0x35 73 | #define CALL 0x36 74 | #define COMO_OPCODE_MAX 0x37 75 | 76 | extern const char * const str_opcodelist[]; 77 | 78 | extern const char *instrstr(ComoOpCode *op); 79 | 80 | //extern ComoOpCode *create_op(unsigned char op, int lineno, Object *arg); 81 | 82 | #endif /* !COMO_OPCODE_H */ 83 | -------------------------------------------------------------------------------- /vm/lang/ast.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define get_node(x) (((node *)(x)) 7 | 8 | #include "graph.h" 9 | 10 | static void visit(node *, int); 11 | 12 | node *create_list(int nchild, int kind, int attrs, char *info, 13 | void(*visitor)(node *, int), void(*attribute_visitor)(node *)) 14 | { 15 | node *n = malloc(sizeof(*n)); 16 | 17 | n->kind = kind; 18 | n->attributes = attrs; 19 | n->line = 1; 20 | n->info = info; 21 | n->visit = visitor; 22 | n->attribute_visit = attribute_visitor; 23 | 24 | n->nchild = nchild; 25 | n->children = malloc(sizeof(*n) * nchild); 26 | 27 | return n; 28 | } 29 | 30 | node *create_root() 31 | { 32 | return create_list(1, COMO_AST_TOP, 0, "Program", visit, NULL); 33 | } 34 | 35 | static void visit_int(node *n, int tabs) 36 | { 37 | int i; 38 | for(i = 0; i < tabs; i++) 39 | fprintf(stdout, " "); 40 | 41 | fprintf(stdout, "Int(%d)\n", ((node_int *)n)->value); 42 | } 43 | 44 | static node *create_int(int val) 45 | { 46 | node_int *n = malloc(sizeof(*n)); 47 | 48 | n->base.kind = COMO_AST_INT; 49 | n->base.attributes = 0; 50 | n->base.line = 1; 51 | n->base.info = "Int"; 52 | n->base.nchild = 0; 53 | n->base.children = NULL; 54 | n->base.visit = visit_int; 55 | 56 | n->value = val; 57 | 58 | return (node *)n; 59 | } 60 | 61 | static void visit_id(node *n, int tabs) 62 | { 63 | int i; 64 | for(i = 0; i < tabs; i++) 65 | fprintf(stdout, " "); 66 | 67 | fprintf(stdout, "Name(%s)\n", ((node_id *)n)->value); 68 | } 69 | 70 | static node *create_id(char *name) 71 | { 72 | node_id *n = malloc(sizeof(*n)); 73 | 74 | n->base.kind = COMO_AST_ID; 75 | n->base.attributes = 0; 76 | n->base.line = 1; 77 | n->base.info = "Name"; 78 | n->base.nchild = 0; 79 | n->base.children = NULL; 80 | n->base.visit = visit_id; 81 | 82 | n->value = malloc(strlen(name) + 1); 83 | 84 | memcpy(n->value, name, strlen(name) + 1); 85 | 86 | return (node *)n; 87 | } 88 | 89 | 90 | static void binop_attribute_visitor(node *n) 91 | { 92 | fprintf(stdout, "%c", n->attributes); 93 | } 94 | 95 | static node *create_binary_op(int op) 96 | { 97 | return create_list(2, COMO_AST_BINOP, op, "BinaryOp", 98 | visit, binop_attribute_visitor); 99 | } 100 | 101 | static void add_child(node *n, int index, node *child) 102 | { 103 | n->children[index] = child; 104 | } 105 | 106 | static void visit(node *node, int tabs) 107 | { 108 | int i; 109 | for(i = 0; i < tabs; i++) 110 | fprintf(stdout, " "); 111 | 112 | fprintf(stdout, "%s", node->info); 113 | 114 | if(node->attribute_visit) 115 | node->attribute_visit(node); 116 | fputc('\n', stdout); 117 | 118 | tabs++; 119 | 120 | if(node->nchild) 121 | { 122 | for(i = 0; i < node->nchild; i++) 123 | node->children[i]->visit(node->children[i], tabs); 124 | } 125 | } 126 | 127 | int main(void) 128 | { 129 | node *root = create_root(1); 130 | node *binop = create_binary_op('+'); 131 | 132 | node *assign = create_binary_op('='); 133 | node *name = create_id("sum"); 134 | 135 | 136 | add_child(binop, 0, create_int(26)); 137 | add_child(binop, 1, create_int( 1)); 138 | 139 | add_child(assign, 0, name); 140 | add_child(assign, 1, binop); 141 | 142 | add_child(root, 0, assign); 143 | 144 | visit(root, 0); 145 | 146 | return 0; 147 | } -------------------------------------------------------------------------------- /vm/simple.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "como_opcode.h" 8 | 9 | /* Opcode 1 byte opcode, 2 byte argument, 1 byte flag 10 | * 11 | * 12 | * 13 | * 14 | * 0x010000FE 15 | * OPCODE ARGUMENT FLAG 16 | * 01 0000 FE 17 | * 18 | * */ 19 | 20 | #define OP(i) ((i) >> 24 & 0xff) 21 | #define OP1(i) ((i) >> 8 & 0xffff) 22 | #define FLAG(i) ((i) & 0xff) 23 | 24 | typedef struct Frame { 25 | long f_co[USHRT_MAX + 1]; /* Constants */ 26 | long f_co_pos; /* constants pointer */ 27 | 28 | int code[255]; /* Bytecode */ 29 | int pc; /* Program counter */ 30 | 31 | 32 | 33 | long stack[255]; /* Value stack */ 34 | long sp; /* Stack pointer */ 35 | } Frame; 36 | 37 | static void init_frame(Frame *frame) 38 | { 39 | memset(frame->f_co, 0, sizeof(frame->f_co)); 40 | frame->f_co_pos = 0; 41 | memset(frame->code, 0, sizeof(frame->code)); 42 | frame->pc = 0; 43 | memset(frame->stack, 0, sizeof(frame->stack)); 44 | frame->sp = 0; 45 | } 46 | 47 | #define TRACE(op) do { \ 48 | printf("%#010X\n", op); \ 49 | } while(0) 50 | 51 | 52 | static int Como_EvalFrameEx(Frame *frame) 53 | { 54 | #define NEXT_OPCODE() ((frame->code[frame->pc++]) >> 24 & 0xff) 55 | #define ARG() ((frame->code[frame->pc - 1]) >> 8 & 0xffff) 56 | #define TARGET(x) case x: 57 | #define VM_DISPATCH() break 58 | #define PUSH(x) frame->stack[frame->sp++] = x 59 | #define POP() frame->stack[--frame->sp] 60 | #define CONSTANT(x) frame->f_co[x] 61 | #define EMPTY() frame->sp == 0 62 | 63 | int retval = 0; 64 | int arg = -1; 65 | int a, b, op; 66 | 67 | for(;;) 68 | { 69 | op = NEXT_OPCODE(); 70 | 71 | TRACE(frame->code[frame->pc - 1]); 72 | 73 | fprintf(stderr, "pc=%d\n", frame->pc); 74 | 75 | switch(op) 76 | { 77 | TARGET(LOAD_CONST) 78 | { 79 | arg = ARG(); 80 | 81 | PUSH(CONSTANT(arg)); 82 | 83 | VM_DISPATCH(); 84 | } 85 | 86 | TARGET(IADD) 87 | { 88 | a = POP(); 89 | b = POP(); 90 | 91 | PUSH(a + b); 92 | 93 | VM_DISPATCH(); 94 | } 95 | 96 | TARGET(IRETURN) 97 | { 98 | 99 | if(!EMPTY()) { 100 | retval = POP(); 101 | } 102 | 103 | VM_DISPATCH(); 104 | } 105 | 106 | TARGET(HALT) 107 | { 108 | goto exit; 109 | } 110 | } 111 | } 112 | 113 | exit: 114 | return retval; 115 | } 116 | 117 | int main(void) 118 | { 119 | 120 | struct Frame frame; 121 | 122 | init_frame(&frame); 123 | 124 | frame.f_co[frame.f_co_pos++] = 150; 125 | frame.code[frame.pc++] = (LOAD_CONST << 24) | ((frame.f_co_pos -1) << 8) | (UCHAR_MAX - 1); 126 | frame.f_co[frame.f_co_pos++] = 50; 127 | frame.code[frame.pc++] = (LOAD_CONST << 24) | ((frame.f_co_pos -1) << 8) | (UCHAR_MAX - 1); 128 | 129 | frame.code[frame.pc++] = (IADD << 24) | ((0) << 8) | (0); 130 | frame.code[frame.pc++] = (IRETURN << 24) | ((0) << 8) | (0); 131 | frame.code[frame.pc++] = (HALT << 24) | ((0) << 8) | (0); 132 | 133 | /* reset the program counter, execute */ 134 | frame.pc = 0; 135 | int retval = Como_EvalFrameEx(&frame); 136 | 137 | fprintf(stdout, "%d\n", retval); 138 | 139 | 140 | #if 0 141 | uint32_t instruction; 142 | 143 | /* LOAD_NAME ARG FLAG */ 144 | instruction = (LOAD_NAME << 24) | (USHRT_MAX << 8) | (UCHAR_MAX - 1); 145 | 146 | fprintf(stdout, 147 | "op=%d, arg=%u, flag=%u\n", 148 | OP(instruction), 149 | OP1(instruction), 150 | FLAG(instruction) 151 | ); 152 | 153 | #endif 154 | 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /como_parser.h: -------------------------------------------------------------------------------- 1 | /* A Bison parser, made by GNU Bison 3.0.4. */ 2 | 3 | /* Bison interface for Yacc-like parsers in C 4 | 5 | Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc. 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . */ 19 | 20 | /* As a special exception, you may create a larger work that contains 21 | part or all of the Bison parser skeleton and distribute that work 22 | under terms of your choice, so long as that work isn't itself a 23 | parser generator using the skeleton or a modified version thereof 24 | as a parser skeleton. Alternatively, if you modify or redistribute 25 | the parser skeleton itself, you may (at your option) remove this 26 | special exception, which will cause the skeleton and the resulting 27 | Bison output files to be licensed under the GNU General Public 28 | License without this special exception. 29 | 30 | This special exception was added by the Free Software Foundation in 31 | version 2.2 of Bison. */ 32 | 33 | #ifndef YY_YY_COMO_PARSER_H_INCLUDED 34 | # define YY_YY_COMO_PARSER_H_INCLUDED 35 | /* Debug traces. */ 36 | #ifndef YYDEBUG 37 | # define YYDEBUG 0 38 | #endif 39 | #if YYDEBUG 40 | extern int yydebug; 41 | #endif 42 | /* "%code requires" blocks. */ 43 | #line 37 "como_parser.y" /* yacc.c:1909 */ 44 | 45 | 46 | extern ast_node *como_parse(const char *filename); 47 | 48 | 49 | #ifndef YY_TYPEDEF_YY_SCANNER_T 50 | #define YY_TYPEDEF_YY_SCANNER_T 51 | typedef void* yyscan_t; 52 | #endif 53 | 54 | 55 | #line 56 "como_parser.h" /* yacc.c:1909 */ 56 | 57 | /* Token type. */ 58 | #ifndef YYTOKENTYPE 59 | # define YYTOKENTYPE 60 | enum yytokentype 61 | { 62 | END = 0, 63 | T_CMP = 258, 64 | T_LTE = 259, 65 | T_AND = 260, 66 | T_NEQ = 261, 67 | T_TYPEOF = 262, 68 | T_GTE = 263, 69 | T_IF = 264, 70 | T_ELSE = 265, 71 | T_WHILE = 266, 72 | T_FOR = 267, 73 | T_FUNC = 268, 74 | T_RETURN = 269, 75 | T_PRINT = 270, 76 | T_NOELSE = 271, 77 | T_INC = 272, 78 | T_DEC = 273, 79 | T_FUNCTION = 274, 80 | T_ASSERT = 275, 81 | T_NUM = 276, 82 | T_ID = 277, 83 | T_STR_LIT = 278 84 | }; 85 | #endif 86 | /* Tokens. */ 87 | #define END 0 88 | #define T_CMP 258 89 | #define T_LTE 259 90 | #define T_AND 260 91 | #define T_NEQ 261 92 | #define T_TYPEOF 262 93 | #define T_GTE 263 94 | #define T_IF 264 95 | #define T_ELSE 265 96 | #define T_WHILE 266 97 | #define T_FOR 267 98 | #define T_FUNC 268 99 | #define T_RETURN 269 100 | #define T_PRINT 270 101 | #define T_NOELSE 271 102 | #define T_INC 272 103 | #define T_DEC 273 104 | #define T_FUNCTION 274 105 | #define T_ASSERT 275 106 | #define T_NUM 276 107 | #define T_ID 277 108 | #define T_STR_LIT 278 109 | 110 | /* Value type. */ 111 | #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED 112 | 113 | union YYSTYPE 114 | { 115 | #line 59 "como_parser.y" /* yacc.c:1909 */ 116 | 117 | long number; 118 | char* id; 119 | char* stringliteral; 120 | ast_node* ast; 121 | 122 | #line 123 "como_parser.h" /* yacc.c:1909 */ 123 | }; 124 | 125 | typedef union YYSTYPE YYSTYPE; 126 | # define YYSTYPE_IS_TRIVIAL 1 127 | # define YYSTYPE_IS_DECLARED 1 128 | #endif 129 | 130 | /* Location type. */ 131 | #if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED 132 | typedef struct YYLTYPE YYLTYPE; 133 | struct YYLTYPE 134 | { 135 | int first_line; 136 | int first_column; 137 | int last_line; 138 | int last_column; 139 | }; 140 | # define YYLTYPE_IS_DECLARED 1 141 | # define YYLTYPE_IS_TRIVIAL 1 142 | #endif 143 | 144 | 145 | 146 | int yyparse (ast_node** ast, yyscan_t scanner); 147 | 148 | #endif /* !YY_YY_COMO_PARSER_H_INCLUDED */ 149 | -------------------------------------------------------------------------------- /como_lexer.l: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * Copyright (c) 2016 Ryan McCullagh 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include "como_globals.h" 24 | #include "como_ast.h" 25 | #include "como_parser.h" 26 | 27 | static void update_loc(YYLTYPE *loc, char* text) 28 | { 29 | loc->first_line = loc->last_line; 30 | loc->first_column = loc->last_column; 31 | int i; 32 | for(i = 0; text[i] != '\0'; i++) 33 | { 34 | if(text[i] == '\n') 35 | { 36 | loc->last_line++; 37 | loc->last_column = 0; 38 | } 39 | else 40 | { 41 | loc->last_column++; 42 | } 43 | } 44 | } 45 | 46 | #define YY_USER_ACTION update_loc(yylloc, yytext); 47 | 48 | %} 49 | 50 | %option outfile="como_lexer.c" header-file="como_lexer.h" 51 | %option warn nodefault 52 | 53 | %option reentrant noyywrap never-interactive nounistd yylineno 54 | %option bison-bridge 55 | %option bison-locations nounput noinput 56 | 57 | %x COMMENT 58 | 59 | WHITE_SPACE [ \r\n\t]* 60 | L [a-zA-Z_] 61 | A [a-zA-Z_0-9] 62 | D [0-9] 63 | 64 | %% 65 | 66 | "//".*\n ; 67 | 68 | "/*" BEGIN(COMMENT); 69 | "/*" ; 70 | "*/" BEGIN(INITIAL); 71 | "EOF" ; 72 | .|"\n" ; 73 | 74 | "assert" { return T_ASSERT; } 75 | "typeof" { return T_TYPEOF; } 76 | "if" { return T_IF; } 77 | "else" { return T_ELSE; } 78 | "while" { return T_WHILE; } 79 | "for" { return T_FOR; } 80 | "func" { return T_FUNC; } 81 | "function" { return T_FUNCTION; } 82 | "print" { return T_PRINT; } 83 | "return" { return T_RETURN; } 84 | "==" { return T_CMP; } 85 | "!=" { return T_NEQ; } 86 | "<=" { return T_LTE; } 87 | ">=" { return T_GTE; } 88 | "++" { return T_INC; } 89 | "--" { return T_DEC; } 90 | "&&" { return T_AND; } 91 | 92 | {WHITE_SPACE} { /* Skipping Blanks Today */ } 93 | {L}{A}* { 94 | size_t len = strlen(yytext); 95 | yylval->id = malloc(len + 1); 96 | memcpy(yylval->id, yytext, len + 1); 97 | return T_ID; 98 | } 99 | 100 | {D}+ { 101 | yylval->number = strtol(yytext, NULL, 10); 102 | return T_NUM; 103 | } 104 | 105 | L?\"(\\.|[^\\"])*\" { 106 | size_t len = strlen(yytext); 107 | if(len > 2U) { 108 | yylval->stringliteral = malloc((len -2) + 1); 109 | memcpy(yylval->stringliteral, yytext + 1, len - 2); 110 | yylval->stringliteral[len - 2] = '\0'; 111 | return T_STR_LIT; 112 | } else { 113 | yylval->stringliteral = malloc(1); 114 | yylval->stringliteral[0] = '\0'; 115 | return T_STR_LIT; 116 | } 117 | } 118 | 119 | L?\'(\\.|[^\\'])*\' { 120 | size_t len = strlen(yytext); 121 | if(len > 2U) { 122 | yylval->stringliteral = malloc((len -2) + 1); 123 | memcpy(yylval->stringliteral, yytext + 1, len - 2); 124 | yylval->stringliteral[len - 2] = '\0'; 125 | return T_STR_LIT; 126 | } else { 127 | yylval->stringliteral = malloc(1); 128 | yylval->stringliteral[0] = '\0'; 129 | return T_STR_LIT; 130 | } 131 | } 132 | 133 | . { return yytext[0]; } 134 | 135 | %% 136 | 137 | -------------------------------------------------------------------------------- /como_executor.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include "como_executor.h" 19 | 20 | static void como_opcode_default_handler(Object *, Object *, Object **); 21 | 22 | #define COMO_OPCODE_HANDLER_INONE como_opcode_default_handler 23 | #define COMO_OPCODE_HANDLER_LOAD_CONST como_opcode_default_handler 24 | #define COMO_OPCODE_HANDLER_STORE_NAME como_opcode_default_handler 25 | #define COMO_OPCODE_HANDLER_LOAD_NAME como_opcode_default_handler 26 | #define COMO_OPCODE_HANDLER_IS_LESS_THAN como_opcode_default_handler 27 | #define COMO_OPCODE_HANDLER_JZ como_opcode_default_handler 28 | #define COMO_OPCODE_HANDLER_IPRINT como_opcode_default_handler 29 | #define COMO_OPCODE_HANDLER_IADD como_opcode_default_handler 30 | #define COMO_OPCODE_HANDLER_JMP como_opcode_default_handler 31 | #define COMO_OPCODE_HANDLER_IRETURN como_opcode_default_handler 32 | #define COMO_OPCODE_HANDLER_NOP como_opcode_default_handler 33 | #define COMO_OPCODE_HANDLER_LABEL como_opcode_default_handler 34 | #define COMO_OPCODE_HANDLER_HALT como_opcode_default_handler 35 | #define COMO_OPCODE_HANDLER_IS_EQUAL como_opcode_default_handler 36 | #define COMO_OPCODE_HANDLER_IDIV como_opcode_default_handler 37 | #define COMO_OPCODE_HANDLER_ITIMES como_opcode_default_handler 38 | #define COMO_OPCODE_HANDLER_IS_GREATER_THAN \ 39 | como_opcode_default_handler 40 | #define COMO_OPCODE_HANDLER_IS_NOT_EQUAL como_opcode_default_handler 41 | #define COMO_OPCODE_HANDLER_IS_GREATER_THAN_OR_EQUAL \ 42 | como_opcode_default_handler 43 | #define COMO_OPCODE_HANDLER_IS_LESS_THAN_OR_EQUAL \ 44 | como_opcode_default_handler 45 | #define COMO_OPCODE_HANDLER_DEFINE_FUNCTION \ 46 | como_opcode_default_handler 47 | #define COMO_OPCODE_HANDLER_CALL_FUNCTION como_opcode_default_handler 48 | #define COMO_OPCODE_HANDLER_POSTFIX_INC como_opcode_default_handler 49 | #define COMO_OPCODE_HANDLER_UNARY_MINUS como_opcode_default_handler 50 | #define COMO_OPCODE_HANDLER_IREM como_opcode_default_handler 51 | #define COMO_OPCODE_HANDLER_POSTFIX_DEC como_opcode_default_handler 52 | 53 | const como_opcode_handler como_opcode_handler_table[COMO_OPCODE_MAX] = { 54 | [INONE] = COMO_OPCODE_HANDLER_INONE, 55 | [LOAD_CONST] = COMO_OPCODE_HANDLER_LOAD_CONST, 56 | [STORE_NAME] = COMO_OPCODE_HANDLER_STORE_NAME, 57 | [LOAD_NAME] = COMO_OPCODE_HANDLER_LOAD_NAME, 58 | [IS_LESS_THAN] = COMO_OPCODE_HANDLER_IS_LESS_THAN, 59 | [JZ] = COMO_OPCODE_HANDLER_JZ, 60 | [IPRINT] = COMO_OPCODE_HANDLER_IPRINT, 61 | [IADD] = COMO_OPCODE_HANDLER_IADD, 62 | [JMP] = COMO_OPCODE_HANDLER_JMP, 63 | [IRETURN] = COMO_OPCODE_HANDLER_IRETURN, 64 | [NOP] = COMO_OPCODE_HANDLER_NOP, 65 | [LABEL] = COMO_OPCODE_HANDLER_LABEL, 66 | [HALT] = COMO_OPCODE_HANDLER_HALT, 67 | [IS_EQUAL] = COMO_OPCODE_HANDLER_IS_EQUAL, 68 | [IDIV] = COMO_OPCODE_HANDLER_IDIV, 69 | [ITIMES] = COMO_OPCODE_HANDLER_ITIMES, 70 | [IS_GREATER_THAN] = COMO_OPCODE_HANDLER_IS_GREATER_THAN, 71 | [IS_NOT_EQUAL] = COMO_OPCODE_HANDLER_IS_NOT_EQUAL, 72 | [IS_GREATER_THAN_OR_EQUAL] = 73 | COMO_OPCODE_HANDLER_IS_GREATER_THAN_OR_EQUAL, 74 | [IS_LESS_THAN_OR_EQUAL] = 75 | COMO_OPCODE_HANDLER_IS_LESS_THAN_OR_EQUAL, 76 | [DEFINE_FUNCTION] = COMO_OPCODE_HANDLER_DEFINE_FUNCTION, 77 | [CALL_FUNCTION] = COMO_OPCODE_HANDLER_CALL_FUNCTION, 78 | [POSTFIX_INC] = COMO_OPCODE_HANDLER_POSTFIX_INC, 79 | [UNARY_MINUS] = COMO_OPCODE_HANDLER_UNARY_MINUS, 80 | [IREM] = COMO_OPCODE_HANDLER_IREM, 81 | [POSTFIX_DEC] = COMO_OPCODE_HANDLER_POSTFIX_DEC, 82 | }; 83 | 84 | static void como_opcode_default_handler(Object *arg1, Object *arg2, 85 | Object **retval) 86 | { 87 | /* TODO */ 88 | } 89 | -------------------------------------------------------------------------------- /vm/lang/grammar2.y: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | #include 4 | 5 | #include "como_parser.h" 6 | #include "como_lexer.h" 7 | 8 | 9 | int yyerror(ast_node **ast, yyscan_t scanner, const char* msg) 10 | { 11 | (void)ast; 12 | fprintf(stderr, "como: error parsing: %s\n", msg); 13 | } 14 | 15 | /* 16 | object literals, 17 | array literals, 18 | functions, 19 | classes with single inheritence, and access modifiers 20 | */ 21 | %} 22 | 23 | %code requires { 24 | #include 25 | 26 | #ifndef YY_TYPEDEF_YY_SCANNER_T 27 | #define YY_TYPEDEF_YY_SCANNER_T 28 | typedef void* yyscan_t; 29 | #endif 30 | 31 | typedef struct ast_node { 32 | void *padding; 33 | } ast_node; 34 | 35 | } 36 | 37 | %union { 38 | long lval; 39 | double dval; 40 | char* id; 41 | char* stringliteral; 42 | ast_node *ast; 43 | } 44 | 45 | %token T_INT 46 | %token T_DOUBLE 47 | %token T_ID 48 | %token T_STR_LIT 49 | %token T_FUNCTION 50 | %token T_CLASS; 51 | %token T_PUBLIC 52 | %token T_PROTECTED 53 | %token T_PRIVATE 54 | %token T_NEW 55 | %token T_IMPORT 56 | 57 | %type function_statement function_statements function_def statements statement program constant primary_expression assignment_expression unary_expression optional_argument_list argument_list argument postfix_expression 58 | 59 | %type key opt_key_vpairs key_vpairs keyvpair 60 | %type class_statements class_statement class_def 61 | %type import_stmt dotted_name import_name 62 | 63 | %start program 64 | 65 | %expect 0 66 | %error-verbose 67 | %defines "como_parser.h" 68 | %pure-parser 69 | 70 | %lex-param { yyscan_t scanner } 71 | %parse-param { ast_node** ast } 72 | %parse-param { yyscan_t scanner } 73 | 74 | %% 75 | 76 | 77 | program: 78 | statements { $$ = $1; } 79 | ; 80 | 81 | statements: 82 | statements statement { $$ = $1; } 83 | | %empty { $$ = (ast_node *)NULL; } 84 | ; 85 | 86 | statement: 87 | assignment_expression ';' { $$ = $1; } 88 | | function_def { $$ = $1; } 89 | | import_stmt optional_semi { $$ = $1; } 90 | | class_def { $$ = $1; } 91 | ; 92 | 93 | optional_semi: 94 | ';' 95 | | %empty 96 | ; 97 | 98 | import_stmt: 99 | T_IMPORT dotted_name { $$ = $2; } 100 | ; 101 | 102 | dotted_name: 103 | import_name { $$ = $1; } 104 | | dotted_name '.' import_name { $$ = $1; } 105 | ; 106 | 107 | import_name: 108 | T_ID { $$ = (ast_node *)NULL; } 109 | ; 110 | 111 | // So we don't have nested functions 112 | function_statements: 113 | function_statements function_statement { $$ = $1; } 114 | | %empty { $$ = (ast_node *)NULL; } 115 | 116 | ; 117 | 118 | function_statement: 119 | assignment_expression ';' { $$ = $1; } 120 | ; 121 | 122 | function_def: 123 | T_FUNCTION T_ID '(' ')' '{' function_statements '}' { $$ = $6; } 124 | ; 125 | 126 | 127 | access_modifier: 128 | T_PUBLIC | T_PROTECTED | T_PRIVATE 129 | ; 130 | 131 | class_statements: 132 | class_statements class_statement { $$ = $1; } 133 | | %empty { $$ = NULL; } 134 | 135 | class_statement: 136 | access_modifier function_def { $$ = $2; } 137 | ; 138 | 139 | class_def: 140 | T_CLASS T_ID '{' class_statements '}' { $$ = $4; } 141 | ; 142 | 143 | primary_expression: 144 | T_ID { $$ = (ast_node *)NULL; } 145 | | constant { $$ = $1; } 146 | // TODO, have a ( function_expression ) here in order to prevent access 147 | // to a function expreesion as FE[0] FE.a 148 | | '(' assignment_expression ')' { $$ = (ast_node *)NULL; } 149 | | '{' opt_key_vpairs '}' { $$ = (ast_node *)NULL; } 150 | | '[' optional_argument_list ']' { $$ = (ast_node *)NULL; } 151 | // TODO possibly a function expression 152 | ; 153 | 154 | opt_key_vpairs: 155 | key_vpairs { $$ = $1; } 156 | | %empty { $$ = (ast_node *)NULL; } 157 | ; 158 | 159 | key_vpairs: 160 | keyvpair { $$ = $1; } 161 | | key_vpairs ',' keyvpair { $$ = $1; } 162 | ; 163 | 164 | keyvpair: 165 | key ':' assignment_expression { $$ = (ast_node *)NULL; } 166 | ; 167 | 168 | key: 169 | T_ID { $$ = (ast_node *)NULL; } 170 | | T_STR_LIT { $$ = (ast_node *)NULL; } 171 | ; 172 | 173 | constant: 174 | T_INT { $$ = (ast_node *)1; } 175 | | T_DOUBLE { $$ = (ast_node *)NULL; } 176 | | T_STR_LIT { $$ = (ast_node *)NULL; } 177 | ; 178 | 179 | assignment_expression: 180 | unary_expression '=' assignment_expression { 181 | // TODO check for valid left hand unary expressions 182 | if($1 == (ast_node *)1) { 183 | fprintf(stderr, "Can't assign to a literal, tried to assign to int\n"); 184 | YYERROR; 185 | } 186 | $$ = (ast_node *)NULL; 187 | } 188 | | T_NEW postfix_expression { $$ = $2; } 189 | | postfix_expression '+' postfix_expression { $$ = $1; } 190 | | postfix_expression '-' postfix_expression { $$ = $1; } 191 | | postfix_expression { $$ = $1; } 192 | ; 193 | 194 | unary_expression: 195 | postfix_expression { $$ = $1; } 196 | ; 197 | 198 | optional_argument_list: 199 | argument_list { $$ = $1; } 200 | | %empty { $$ = (ast_node *)NULL; } 201 | ; 202 | 203 | argument_list: 204 | argument { $$ = (ast_node *)NULL; } 205 | | argument_list ',' argument { $$ = $1; } 206 | ; 207 | 208 | argument: 209 | assignment_expression { $$ = (ast_node *)NULL; } 210 | ; 211 | 212 | postfix_expression: 213 | primary_expression { $$ = $1; } 214 | | postfix_expression '[' assignment_expression ']' { $$ = (ast_node *)NULL; } 215 | | postfix_expression '(' optional_argument_list ')' { $$ = (ast_node *)NULL; } 216 | | postfix_expression '.' T_ID { $$ = (ast_node *)NULL; } 217 | ; 218 | 219 | %% 220 | 221 | #include "como_lexer.h" 222 | #include "io.c" 223 | 224 | ast_node *como_parse(const char *filename) 225 | { 226 | ast_node* statements; 227 | yyscan_t scanner; 228 | YY_BUFFER_STATE state; 229 | char* text; 230 | text = como_read_file(filename); 231 | if(text == NULL) 232 | { 233 | goto fail; 234 | } 235 | 236 | if(yylex_init(&scanner)) 237 | { 238 | goto fail; 239 | } 240 | state = yy_scan_string(text, scanner); 241 | if(yyparse(&statements, scanner)) 242 | { 243 | goto fail; 244 | } 245 | yy_delete_buffer(state, scanner); 246 | yylex_destroy(scanner); 247 | free(text); 248 | return statements; 249 | fail: 250 | fprintf(stdout, "Error parsing file '%s'\n", filename); 251 | fflush(stdout); 252 | exit(1); 253 | } 254 | 255 | 256 | int main(int argc, char **argv) 257 | { 258 | ast_node *program = como_parse(argv[1]); 259 | 260 | return 0; 261 | } 262 | -------------------------------------------------------------------------------- /como_ast.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #ifndef COMO_AST_H 19 | #define COMO_AST_H 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | typedef enum 26 | { 27 | AST_NODE_TYPE_NUMBER, 28 | AST_NODE_TYPE_STRING, 29 | AST_NODE_TYPE_ID, 30 | AST_NODE_TYPE_FOR, 31 | AST_NODE_TYPE_STATEMENT_LIST, 32 | AST_NODE_TYPE_BIN_OP, 33 | AST_NODE_TYPE_IF, 34 | AST_NODE_TYPE_WHILE, 35 | AST_NODE_TYPE_FUNC_DECL, 36 | AST_NODE_TYPE_CALL, 37 | AST_NODE_TYPE_RET, 38 | AST_NODE_TYPE_PRINT, 39 | AST_NODE_TYPE_UNARY_OP, 40 | AST_NODE_TYPE_POSTFIX, 41 | AST_NODE_TYPE_TYPEOF, 42 | AST_NODE_TYPE_ASSERT, 43 | AST_NODE_TYPE_BOOL, 44 | AST_NODE_TYPE_ARRAY, 45 | AST_NODE_TYPE_SLOT_ACCESS, 46 | } ast_node_type; 47 | 48 | typedef enum 49 | { 50 | AST_POSTFIX_OP_INC, 51 | AST_POSTFIX_OP_DEC, 52 | } ast_postfix_op_type; 53 | 54 | typedef enum { 55 | AST_UNARY_OP_MINUS, 56 | AST_UNARY_NOT, 57 | } ast_unary_op_type; 58 | 59 | typedef enum { 60 | AST_BINARY_OP_ADD, 61 | AST_BINARY_OP_MINUS, 62 | AST_BINARY_OP_ASSIGN, 63 | AST_BINARY_OP_TIMES, 64 | AST_BINARY_OP_DIV, 65 | AST_BINARY_OP_CMP, 66 | AST_BINARY_OP_LT, 67 | AST_BINARY_OP_LTE, 68 | AST_BINARY_OP_GT, 69 | AST_BINARY_OP_GTE, 70 | AST_BINARY_OP_NEQ, 71 | AST_BINARY_OP_REM, 72 | AST_BINARY_OP_AND, 73 | } ast_binary_op_type; 74 | 75 | typedef struct ast_node ast_node; 76 | 77 | typedef struct 78 | { 79 | char *name; 80 | size_t length; 81 | } ast_node_id; 82 | 83 | typedef struct ast_node_statements ast_node_statements; 84 | 85 | struct ast_node_statements 86 | { 87 | size_t count; 88 | size_t capacity; 89 | ast_node **statement_list; 90 | }; 91 | 92 | typedef struct 93 | { 94 | ast_binary_op_type type; 95 | ast_node *left; 96 | ast_node *right; 97 | } ast_node_binary; 98 | 99 | typedef struct 100 | { 101 | ast_unary_op_type type; 102 | ast_node *expr; 103 | } ast_node_unary; 104 | 105 | typedef struct 106 | { 107 | ast_postfix_op_type type; 108 | ast_node *expr; 109 | } ast_node_postfix; 110 | 111 | typedef struct 112 | { 113 | ast_node *condition; 114 | ast_node *b1; 115 | ast_node *b2; 116 | } ast_node_if; 117 | 118 | typedef struct 119 | { 120 | ast_node *condition; 121 | ast_node *body; 122 | } ast_node_while; 123 | 124 | typedef struct 125 | { 126 | ast_node *initialization; 127 | ast_node *condition; 128 | ast_node *final_expression; 129 | ast_node *body; 130 | } ast_node_for; 131 | 132 | typedef struct 133 | { 134 | char *name; 135 | size_t name_length; 136 | ast_node *parameter_list; 137 | ast_node *body; 138 | } ast_node_function; 139 | 140 | typedef struct 141 | { 142 | int lineno; 143 | int colno; 144 | ast_node *id; 145 | ast_node *arguments; 146 | } ast_node_call; 147 | 148 | typedef struct 149 | { 150 | ast_node *expr; 151 | } ast_node_return; 152 | 153 | typedef struct 154 | { 155 | ast_node *expr; 156 | } ast_node_print; 157 | 158 | typedef struct 159 | { 160 | ast_node *expr; 161 | } ast_node_typeof; 162 | 163 | typedef struct 164 | { 165 | int lineno; 166 | ast_node *expr; 167 | } ast_node_assert; 168 | 169 | typedef struct 170 | { 171 | ast_node *value; 172 | ast_node *index; 173 | } ast_node_slot_access; 174 | 175 | typedef struct 176 | { 177 | ast_node *elements; 178 | } ast_node_type_array; 179 | 180 | struct ast_node 181 | { 182 | ast_node_type type; 183 | union 184 | { 185 | long number_value; 186 | struct 187 | { 188 | char *value; 189 | size_t length; 190 | } string_value; 191 | ast_node_id id_node; 192 | ast_node_statements statements_node; 193 | ast_node_binary binary_node; 194 | ast_node_unary unary_node; 195 | ast_node_if if_node; 196 | ast_node_while while_node; 197 | ast_node_for for_node; 198 | ast_node_function function_node; 199 | ast_node_call call_node; 200 | ast_node_return return_node; 201 | ast_node_print print_node; 202 | ast_node_postfix postfix_node; 203 | ast_node_typeof typeof_node; 204 | ast_node_assert assert_node; 205 | ast_node_type_array array_node; 206 | ast_node_slot_access slot_access_node; 207 | } u1; 208 | }; 209 | 210 | #define AST_NODE_AS_ID(p) (p)->u1.id_node.name 211 | 212 | extern void ast_node_free(ast_node *p); 213 | 214 | ast_node *ast_node_create_slot_access(ast_node *value, ast_node *index); 215 | 216 | ast_node *ast_node_create_array(ast_node *elements); 217 | 218 | ast_node *ast_node_create_assert(ast_node *expression, int lineno); 219 | 220 | ast_node *ast_node_create_tyepof(ast_node *expression); 221 | 222 | extern ast_node *ast_node_create_postfix_op(ast_postfix_op_type type, 223 | ast_node *expression); 224 | 225 | extern ast_node *ast_node_create_unary_op(ast_unary_op_type, ast_node *); 226 | 227 | extern ast_node *ast_node_create_number(long value); 228 | 229 | extern ast_node *ast_node_create_statement_list(size_t count, ...); 230 | 231 | extern void ast_node_statement_list_push(ast_node *node, ast_node *value); 232 | 233 | extern ast_node *ast_node_create_binary_op(ast_binary_op_type type, 234 | ast_node *left, ast_node *right); 235 | 236 | extern ast_node *ast_node_create_id(const char *name); 237 | 238 | extern ast_node *ast_node_create_if(ast_node *condition, ast_node *b1, 239 | ast_node *b2); 240 | 241 | extern ast_node *ast_node_create_while(ast_node *condition, ast_node *body); 242 | 243 | extern ast_node *ast_node_create_for(ast_node *initialization, 244 | ast_node *condition, ast_node *final_expression, ast_node *body); 245 | 246 | extern ast_node *ast_node_create_function(const char *name, 247 | ast_node *parameters, ast_node *body); 248 | 249 | extern ast_node *ast_node_create_call(ast_node *id, ast_node *arguments, 250 | int lineno, int col); 251 | 252 | extern ast_node *ast_node_create_return(ast_node *expr); 253 | 254 | extern ast_node *ast_node_create_print(ast_node *expr); 255 | 256 | extern ast_node *ast_node_create_string_literal(const char *str); 257 | 258 | extern void ast_node_free(ast_node *node); 259 | 260 | extern void ast_node_dump_tree(ast_node *node); 261 | 262 | #endif /* !COMO_AST_H */ 263 | -------------------------------------------------------------------------------- /vm/lang/grammar.y: -------------------------------------------------------------------------------- 1 | %{ 2 | 3 | #include 4 | 5 | #include "como_parser.h" 6 | #include "como_lexer.h" 7 | 8 | 9 | int yyerror(YYLTYPE *loc, ast_node **ast, 10 | yyscan_t scanner, const char* msg) 11 | { 12 | (void)ast; 13 | (void)scanner; 14 | fprintf(stderr, "como: syntax error: %s on line %d\n", 15 | msg, loc->first_line); 16 | 17 | return 1; 18 | } 19 | 20 | /* 21 | object literals, 22 | array literals, 23 | functions, 24 | classes with single inheritence, and access modifiers 25 | */ 26 | %} 27 | 28 | %code requires { 29 | #include 30 | 31 | #ifndef YY_TYPEDEF_YY_SCANNER_T 32 | #define YY_TYPEDEF_YY_SCANNER_T 33 | typedef void* yyscan_t; 34 | #endif 35 | 36 | typedef struct ast_node { 37 | void *padding; 38 | } ast_node; 39 | 40 | } 41 | 42 | %union { 43 | long lval; 44 | double dval; 45 | char* id; 46 | char* stringliteral; 47 | ast_node *ast; 48 | } 49 | 50 | %token T_INT 51 | %token T_DOUBLE 52 | %token T_ID 53 | %token T_STR_LIT 54 | 55 | %token keyword 56 | %token id 57 | %token stringliteral 58 | 59 | %token T_FUNCTION "'function' keyword" 60 | %token T_CLASS "class" 61 | %token T_PUBLIC "keyword 'public'" 62 | %token T_PROTECTED "protected" 63 | %token T_PRIVATE "private" 64 | %token T_NEW "new" 65 | %token T_IMPORT "import" 66 | 67 | %printer { 68 | fprintf(yyoutput, "keyword '%s'", $$); 69 | } keyword 70 | 71 | %destructor { 72 | fprintf(stdout, "Freeing id or string literal"); 73 | } id stringliteral 74 | 75 | %token END 0 "EOF" 76 | 77 | 78 | %type function_statement function_statements function_def statements statement program constant primary_expression assignment_expression unary_expression optional_argument_list argument_list argument postfix_expression 79 | 80 | %type key opt_key_vpairs key_vpairs keyvpair 81 | %type class_statements class_statement class_def 82 | %type import_stmt dotted_name import_name 83 | 84 | %start program 85 | 86 | %expect 0 87 | %error-verbose 88 | %defines "como_parser.h" 89 | %pure-parser 90 | %locations 91 | 92 | %lex-param { yyscan_t scanner } 93 | %parse-param { ast_node** ast } 94 | %parse-param { yyscan_t scanner } 95 | 96 | %% 97 | 98 | 99 | program: 100 | statements { $$ = $1; } 101 | ; 102 | 103 | statements: 104 | statements statement { $$ = $1; } 105 | | %empty { $$ = (ast_node *)NULL; } 106 | ; 107 | 108 | statement: 109 | assignment_expression ';' { $$ = $1; } 110 | | function_def { $$ = $1; } 111 | | import_stmt optional_semi { $$ = $1; } 112 | | class_def { $$ = $1; } 113 | // selection statements, (while, for, if, elseif, else 114 | ; 115 | 116 | optional_semi: 117 | ';' 118 | | %empty 119 | ; 120 | 121 | import_stmt: 122 | T_IMPORT dotted_name { $$ = $2; } 123 | ; 124 | 125 | dotted_name: 126 | // Each dotted name can be a directory, until the last 127 | // one where it should be a file 128 | import_name { $$ = $1; } 129 | | dotted_name '.' import_name { $$ = $1; } 130 | ; 131 | 132 | import_name: 133 | T_ID { $$ = (ast_node *)NULL; } 134 | ; 135 | 136 | // So we don't have nested functions 137 | function_statements: 138 | function_statements function_statement { $$ = $1; } 139 | | %empty { $$ = (ast_node *)NULL; } 140 | ; 141 | 142 | function_statement: 143 | assignment_expression ';' { $$ = $1; } 144 | ; 145 | 146 | function_def: 147 | T_FUNCTION T_ID '(' ')' '{' function_statements '}' { $$ = $6; } 148 | ; 149 | 150 | access_modifier: 151 | T_PUBLIC | T_PROTECTED | T_PRIVATE 152 | ; 153 | 154 | class_statements: 155 | class_statements class_statement { $$ = $1; } 156 | | %empty { $$ = NULL; } 157 | 158 | class_statement: 159 | access_modifier function_def { $$ = $2; } 160 | ; 161 | 162 | class_def: 163 | T_CLASS T_ID '{' class_statements '}' { $$ = $4; } 164 | ; 165 | 166 | primary_expression: 167 | T_ID { $$ = (ast_node *)NULL; } 168 | | constant { $$ = $1; } 169 | // TODO, have a ( function_expression ) here in order to prevent access 170 | // to a function expreesion as FE[0] FE.a 171 | | '(' assignment_expression ')' { $$ = (ast_node *)NULL; } 172 | | '{' opt_key_vpairs '}' { $$ = (ast_node *)NULL; } 173 | | '[' optional_argument_list ']' { $$ = (ast_node *)NULL; } 174 | // TODO possibly a function expression 175 | ; 176 | 177 | opt_key_vpairs: 178 | key_vpairs { $$ = $1; } 179 | | %empty { $$ = (ast_node *)NULL; } 180 | ; 181 | 182 | key_vpairs: 183 | keyvpair { $$ = $1; } 184 | | key_vpairs ',' keyvpair { $$ = $1; } 185 | ; 186 | 187 | keyvpair: 188 | key ':' assignment_expression { $$ = (ast_node *)NULL; } 189 | ; 190 | 191 | key: 192 | T_ID { $$ = (ast_node *)NULL; } 193 | | T_STR_LIT { $$ = (ast_node *)NULL; } 194 | ; 195 | 196 | constant: 197 | T_INT { $$ = (ast_node *)1; } 198 | | T_DOUBLE { $$ = (ast_node *)NULL; } 199 | | T_STR_LIT { $$ = (ast_node *)NULL; } 200 | ; 201 | 202 | assignment_expression: 203 | unary_expression '=' assignment_expression { 204 | // TODO check for valid left hand unary expressions 205 | if($1 == (ast_node *)1) { 206 | fprintf(stderr, "Can't assign to a literal, tried to assign to int\n"); 207 | YYERROR; 208 | } 209 | $$ = (ast_node *)NULL; 210 | } 211 | | T_NEW postfix_expression { $$ = $2; } 212 | | postfix_expression '+' postfix_expression { $$ = $1; } 213 | | postfix_expression '-' postfix_expression { $$ = $1; } 214 | | postfix_expression { $$ = $1; } 215 | ; 216 | 217 | unary_expression: 218 | postfix_expression { $$ = $1; } 219 | ; 220 | 221 | optional_argument_list: 222 | argument_list { $$ = $1; } 223 | | %empty { $$ = (ast_node *)NULL; } 224 | ; 225 | 226 | argument_list: 227 | argument { $$ = (ast_node *)NULL; } 228 | | argument_list ',' argument { $$ = $1; } 229 | ; 230 | 231 | argument: 232 | assignment_expression { $$ = (ast_node *)NULL; } 233 | ; 234 | 235 | postfix_expression: 236 | primary_expression { $$ = $1; } 237 | | postfix_expression '[' assignment_expression ']' { $$ = (ast_node *)NULL; } 238 | | postfix_expression '(' optional_argument_list ')' { $$ = (ast_node *)NULL; } 239 | | postfix_expression '.' T_ID { $$ = (ast_node *)NULL; } 240 | ; 241 | 242 | %% 243 | 244 | #include "como_lexer.h" 245 | #include "io.c" 246 | 247 | ast_node *como_parse(const char *filename) 248 | { 249 | ast_node* statements; 250 | yyscan_t scanner; 251 | YY_BUFFER_STATE state; 252 | char* text; 253 | text = como_read_file(filename); 254 | if(text == NULL) 255 | { 256 | goto fail; 257 | } 258 | 259 | if(yylex_init(&scanner)) 260 | { 261 | goto fail; 262 | } 263 | state = yy_scan_string(text, scanner); 264 | if(yyparse(&statements, scanner)) 265 | { 266 | goto fail; 267 | } 268 | yy_delete_buffer(state, scanner); 269 | yylex_destroy(scanner); 270 | free(text); 271 | return statements; 272 | fail: 273 | fprintf(stdout, "Error parsing file '%s'\n", filename); 274 | fflush(stdout); 275 | exit(1); 276 | } 277 | 278 | 279 | int main(int argc, char **argv) 280 | { 281 | (void)argc; 282 | ast_node *program = como_parse(argv[1]); 283 | (void)program; 284 | return 0; 285 | } 286 | 287 | -------------------------------------------------------------------------------- /2: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define COMO_COMPILER 1 8 | 9 | #include "como_ast.h" 10 | #include "como_stack.h" 11 | #include "como_debug.h" 12 | #include "como_opcode.h" 13 | #include "como_globals.h" 14 | #include "como_parser.h" 15 | #include "como_lexer.h" 16 | #include "como_compiler.h" 17 | #include "como_io.h" 18 | #include "como_object.h" 19 | 20 | static ComoFrame *global_frame = NULL; 21 | 22 | static ComoOpCode *create_op(unsigned char op, Object *oper) 23 | { 24 | ComoOpCode *ret = malloc(sizeof(ComoOpCode)); 25 | ret->op_code = op; 26 | 27 | ret->flags = 0; 28 | 29 | if(oper != NULL) { 30 | ret->flags |= COMO_OP_CODE_OPERAND_USED; 31 | } 32 | 33 | // TODO, line numbers 34 | 35 | ret->operand = oper; 36 | return ret; 37 | } 38 | 39 | static ComoFrame *create_frame(Object *code, const char *name) 40 | { 41 | size_t i; 42 | ComoFrame *frame = malloc(sizeof(ComoFrame)); 43 | 44 | frame->cf_sp = 0; 45 | frame->cf_stack_size = 0; 46 | 47 | for(i = 0; i < (size_t)COMO_DEFAULT_FRAME_STACKSIZE; i++) { 48 | frame->cf_stack[i] = NULL; 49 | } 50 | 51 | frame->cf_symtab = newMap(4); 52 | frame->code = code; 53 | frame->pc = 0; 54 | frame->next = NULL; 55 | frame->namedparameters = newArray(2); 56 | frame->filename = NULL; 57 | frame->call_stack = NULL; 58 | frame->name = newString(name); 59 | frame->caller = NULL; 60 | return frame; 61 | } 62 | 63 | static int exception = 0; 64 | static char exmessage[1024]; 65 | 66 | #define Como_SetError(fmt, ...) do { \ 67 | exception = 1; \ 68 | memset(exmessage, 0, sizeof(exmessage)); \ 69 | sprintf(exmessage, fmt, __VA_ARGS__); \ 70 | } while(0) 71 | 72 | /* VM main loop */ 73 | Object *Como_EvalFrame(ComoFrame *frame) 74 | { 75 | #define O_INCRREF(O) \ 76 | O_REFCNT((O))++ 77 | 78 | #define O_DECREF(O) do { \ 79 | if(--O_REFCNT((O)) == 0) { \ 80 | objectDestroy((O)); \ 81 | } \ 82 | } while(0) 83 | 84 | #define OPR_DECREF(Opcode) do { \ 85 | (Opcode)->flags |= COMO_OP_CODE_OPERAND_FREE; \ 86 | objectDestroy((Opcode)->operand); \ 87 | (Opcode)->operand = NULL; \ 88 | } while(0) 89 | 90 | #define TARGET(Name) \ 91 | case Name: 92 | 93 | #define VM_CONTINUE() \ 94 | goto next_opcode 95 | 96 | #define VM_DISPATCH() \ 97 | break 98 | 99 | #define NEXT_OPCODE() \ 100 | (ComoOpCode *)O_PTVAL(O_AVAL((frame)->code)->table[(frame)->pc++]); 101 | 102 | #define POP() \ 103 | frame->cf_stack[--frame->cf_sp] 104 | 105 | #define PUSH(Obj) \ 106 | frame->cf_stack[frame->cf_sp++] = Obj 107 | 108 | #define OP1() \ 109 | opcode->operand 110 | 111 | Object *retval = NULL; 112 | 113 | for(;;) 114 | { 115 | ComoOpCode *opcode; 116 | next_opcode: 117 | opcode = NEXT_OPCODE(); 118 | 119 | #ifdef COMO_DEBUG 120 | fprintf(stderr, "%s\n", instrstr(opcode)); 121 | #endif 122 | 123 | switch(opcode->op_code) 124 | { 125 | TARGET(HALT) 126 | { 127 | return retval; 128 | } 129 | 130 | TARGET(LOAD_NAME) 131 | { 132 | Object *arg = OP1(); 133 | Object *value = 134 | mapSearchEx(frame->cf_symtab, O_SVAL(arg)->value); 135 | 136 | if(value == NULL) { 137 | Como_SetError( 138 | "Undefined symbol, %s", O_SVAL(arg)->value 139 | ); 140 | } else { 141 | 142 | O_INCRREF(value); 143 | 144 | PUSH(value); 145 | 146 | OPR_DECREF(opcode); 147 | } 148 | 149 | VM_DISPATCH(); 150 | } 151 | 152 | TARGET(LOAD_CONST) 153 | { 154 | Object *arg = OP1(); 155 | PUSH(arg); 156 | O_INCRREF(arg); 157 | 158 | VM_DISPATCH(); 159 | } 160 | 161 | TARGET(STORE_NAME) 162 | { 163 | Object *value = POP(); 164 | Object *name = OP1(); 165 | 166 | mapInsertEx(frame->cf_symtab, O_SVAL(name)->value, value); 167 | 168 | O_INCRREF(value); 169 | 170 | OPR_DECREF(opcode); 171 | 172 | VM_DISPATCH(); 173 | } 174 | TARGET(IADD) 175 | { 176 | Object *right = POP(); 177 | Object *left = POP(); 178 | 179 | if(Obj_CheckExact(right, IS_LONG) && 180 | Obj_CheckExact(left, IS_LONG)) 181 | { 182 | long result = O_LVAL(left) + O_LVAL(right); 183 | 184 | O_DECREF(left); 185 | O_DECREF(right); 186 | 187 | PUSH(newLong(result)); 188 | } 189 | else 190 | { 191 | /* Temp char buffers */ 192 | char *_s1 = objectToString(left); 193 | char *_s2 = objectToString(right); 194 | 195 | Object *s1 = newString(_s1); 196 | Object *s2 = newString(_s2); 197 | 198 | Object *result = stringCat(s1, s2); 199 | 200 | free(_s1); 201 | free(_s2); 202 | 203 | O_DECREF(s1); 204 | O_DECREF(s2); 205 | 206 | PUSH(result); 207 | } 208 | 209 | VM_DISPATCH(); 210 | } 211 | } 212 | 213 | if(exception) { 214 | como_error_noreturn(frame, exmessage); 215 | } 216 | } 217 | 218 | return retval; 219 | } 220 | 221 | 222 | static void destroy_frame(ComoFrame *frame) 223 | { 224 | uint32_t i; 225 | 226 | objectDestroy(frame->lineno); 227 | objectDestroy(frame->filename); 228 | objectDestroy(frame->cf_symtab); 229 | objectDestroy(frame->namedparameters); 230 | objectDestroy(frame->name); 231 | 232 | for(i = 0; i < O_AVAL(frame->code)->size; i++) 233 | { 234 | ComoOpCode *op = (ComoOpCode *)O_PTVAL((O_AVAL(frame->code))->table[i]); 235 | 236 | if((op->flags & COMO_OP_CODE_OPERAND_USED) && 237 | !(op->flags & COMO_OP_CODE_OPERAND_FREE)) 238 | { 239 | objectDestroy(op->operand); 240 | } 241 | 242 | free(op); 243 | } 244 | 245 | objectDestroy(frame->code); 246 | 247 | fprintf(stdout, " --- Stack Pointer: %d\n", (int)frame->cf_sp); 248 | fprintf(stdout, " --- Program Counter: %d\n", (int)frame->pc); 249 | 250 | free(frame); 251 | 252 | } 253 | 254 | static void _como_compile_ast(ast_node *p, const char *filename, int dump_asm) { 255 | Object *main_code = newArray(4); 256 | 257 | global_frame = create_frame(main_code, "__main__"); 258 | 259 | global_frame->lineno = newLong(0L); 260 | global_frame->filename = newString(filename); 261 | 262 | como_compile(p, global_frame); 263 | 264 | arrayPushEx(main_code, newPointer((void *)create_op(HALT, NULL))); 265 | 266 | if(!dump_asm) 267 | (void)Como_EvalFrame(global_frame); 268 | 269 | destroy_frame(global_frame); 270 | } 271 | 272 | 273 | void como_compile_ast(ast_node *p, const char *filename) { 274 | _como_compile_ast(p, filename, 0); 275 | } 276 | 277 | 278 | char *get_active_file_name(void) { 279 | return "-"; 280 | } 281 | -------------------------------------------------------------------------------- /vm/eval.c: -------------------------------------------------------------------------------- 1 | #define vm_case(o) switch(o) 2 | 3 | #define vm_target(x) case x: 4 | 5 | #define vm_continue() break 6 | 7 | #define fetch() (frame->pc++, (((como_code_get(frame->code, frame->pc -1) >> 24) & 0xff))) 8 | 9 | #define get_const(x) \ 10 | como_array_get(frame->constants, x) 11 | 12 | #define get_arg() \ 13 | ((como_code_get(frame->code, frame->pc -1) >> 8) & 0xffff) 14 | 15 | #define push(arg) \ 16 | (should_grow(frame), (frame->stack[frame->sp++] = arg)) 17 | 18 | #define pop() \ 19 | (frame->stack[--frame->sp]) 20 | 21 | #define empty() \ 22 | (frame->sp == 0) 23 | 24 | #define getflag() (((como_code_get(frame->code, frame->pc -1)) & 0xff)) 25 | 26 | como_object *arg; 27 | como_object *retval = NULL; 28 | como_object *left, *right, *result; 29 | char *ex = NULL; 30 | 31 | ((como_object *)frame)->type->obj_init((como_object *)frame); 32 | 33 | #define set_except(fmt, ...) \ 34 | ex = make_except(fmt, ##__VA_ARGS__) 35 | 36 | for(;;) { 37 | top: 38 | /* Reset result */ 39 | result = NULL; 40 | vm_case(fetch()) { 41 | vm_target(JMP) { 42 | frame->pc = get_arg(); 43 | goto top; 44 | } 45 | vm_target(JZ) { 46 | result = pop(); 47 | if(!truthy(result)) { 48 | frame->pc = (como_size_t)get_arg(); 49 | goto top; 50 | } 51 | vm_continue(); 52 | } 53 | vm_target(LOAD_CONST) { 54 | arg = get_const(get_arg()); 55 | push(arg); 56 | vm_continue(); 57 | } 58 | vm_target(STORE_NAME) { 59 | arg = get_const(get_arg()); 60 | result = pop(); 61 | como_map_put(frame->locals, arg, result); 62 | decref(arg); 63 | //incref(result); 64 | push(result); 65 | vm_continue(); 66 | } 67 | vm_target(LOAD_NAME){ 68 | arg = get_const(get_arg()); 69 | result = como_map_get(frame->locals, arg); 70 | if(result) { 71 | push(result); 72 | } 73 | else { 74 | set_except("NameError, undefined variable `%s`", ((como_string *)arg)->value); 75 | } 76 | decref(arg); 77 | vm_continue(); 78 | } 79 | vm_target(IADD) { 80 | right = pop(); 81 | left = pop(); 82 | result = add(frame, left, right); 83 | 84 | decref(right); 85 | decref(left); 86 | 87 | if(result) { 88 | push(result); 89 | } 90 | else 91 | set_except("unsupported operands for + operator"); 92 | vm_continue(); 93 | } 94 | vm_target(IMINUS) { 95 | right = pop(); 96 | left = pop(); 97 | result = sub(frame, left, right); 98 | 99 | decref(right); 100 | decref(left); 101 | 102 | if(result) { 103 | push(result); 104 | } 105 | else 106 | set_except("unsupported operands for - operator"); 107 | vm_continue(); 108 | } 109 | vm_target(ITIMES) { 110 | right = pop(); 111 | left = pop(); 112 | result = mul(frame, left, right); 113 | 114 | decref(right); 115 | decref(left); 116 | 117 | if(result) { 118 | push(result); 119 | } 120 | else 121 | set_except("unsupported operands for * operator"); 122 | vm_continue(); 123 | } 124 | vm_target(IDIV) { 125 | right = pop(); 126 | left = pop(); 127 | result = do_div(frame, left, right); 128 | 129 | decref(right); 130 | decref(left); 131 | 132 | if(result) { 133 | push(result); 134 | } 135 | else 136 | set_except("unsupported operands for / operator"); 137 | vm_continue(); 138 | } 139 | vm_target(IREM) { 140 | right = pop(); 141 | left = pop(); 142 | result = rem(frame, left, right); 143 | 144 | decref(right); 145 | decref(left); 146 | 147 | if(result) { 148 | push(result); 149 | } 150 | else 151 | set_except("unsupported operands for % operator"); 152 | vm_continue(); 153 | } 154 | vm_target(UNARY_MINUS) { 155 | left = pop(); 156 | result = unaryminus(frame, left); 157 | if(result) 158 | push(result); 159 | else 160 | set_except("unsupported operand for unary -"); 161 | vm_continue(); 162 | } 163 | vm_target(UNARY_PLUS) { 164 | left = pop(); 165 | result = unaryplus(frame, left); 166 | if(result) 167 | push(result); 168 | else 169 | set_except("unsupported operand for unary +"); 170 | vm_continue(); 171 | } 172 | vm_target(IRETURN) { 173 | /* getflag() determine if the frame is returning a value */ 174 | if(getflag() && !empty()) 175 | retval = pop(); 176 | goto exit; 177 | } 178 | vm_target(IPRINT) { 179 | result = pop(); 180 | como_object_print(result); 181 | decref(result); 182 | fputc('\n', stdout); 183 | vm_continue(); 184 | } 185 | vm_target(EQUAL) { 186 | right = pop(); 187 | left = pop(); 188 | result = isequal(frame, left, right); 189 | 190 | decref(right); 191 | decref(left); 192 | 193 | push(result); 194 | vm_continue(); 195 | } 196 | vm_target(NEQUAL) { 197 | right = pop(); 198 | left = pop(); 199 | result = nequal(frame, left, right); 200 | push(result); 201 | vm_continue(); 202 | } 203 | vm_target(GT) { 204 | right = pop(); 205 | left = pop(); 206 | result = isgt(frame, left, right); 207 | push(result); 208 | vm_continue(); 209 | } 210 | vm_target(LT) { 211 | right = pop(); 212 | left = pop(); 213 | result = islt(frame, left, right); 214 | push(result); 215 | vm_continue(); 216 | } 217 | vm_target(GTE) { 218 | right = pop(); 219 | left = pop(); 220 | result = isgte(frame, left, right); 221 | push(result); 222 | vm_continue(); 223 | } 224 | vm_target(LTE) { 225 | right = pop(); 226 | left = pop(); 227 | result = islte(frame, left, right); 228 | 229 | if(result) 230 | push(result); 231 | else 232 | set_except("unsupported operand types for <= operator"); 233 | 234 | decref(right); 235 | decref(left); 236 | vm_continue(); 237 | } 238 | } 239 | 240 | if(ex) { 241 | /* Find the exception handler */ 242 | como_size_t cpc = frame->pc; 243 | while(--cpc) 244 | { 245 | como_uint32_t opline; 246 | opline = como_code_get(frame->code, cpc); 247 | if(((opline >> 24) & 0xff) == TRY) 248 | { 249 | frame->pc = (opline >> 8) & 0xffff; 250 | 251 | /* Now, determine what the variable name is for the catch statement */ 252 | como_uint32_t nextop = como_code_get(frame->code, frame->pc + 1); 253 | if(((nextop >> 24) & 0xff) == LOAD_NAME) 254 | { 255 | como_size_t constindex = (nextop >> 8) & 0xffff; 256 | como_object *name = get_const(constindex); 257 | como_map_put(frame->locals, name, gc_new(frame, como_stringfromstring( 258 | (char *)ex))); 259 | } 260 | free(ex); 261 | ex = NULL; 262 | goto top; 263 | } 264 | } 265 | fprintf(stdout, "como: fatal, unhandled exception: %s\n", ex); 266 | free(ex); 267 | ex = NULL; 268 | goto exit; 269 | } 270 | } 271 | 272 | exit: 273 | while(!empty()) { 274 | como_object *obj = pop(); 275 | como_warning("%p was on stack at end of frame eval, refcount at %ld", 276 | (void *)obj, obj->flags); 277 | decref(obj); 278 | } 279 | 280 | do_gc(frame); 281 | 282 | ((como_object *)frame)->type->obj_deinit((como_object *)frame); 283 | 284 | como_object_dtor((como_object *)frame); 285 | 286 | return retval; -------------------------------------------------------------------------------- /como_ast.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Ryan McCullagh 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "como_ast.h" 24 | #include "como_globals.h" 25 | 26 | ast_node *ast_node_create_slot_access(ast_node *value, ast_node *index) 27 | { 28 | ast_node *retval = malloc(sizeof(ast_node)); 29 | retval->type = AST_NODE_TYPE_SLOT_ACCESS; 30 | retval->u1.slot_access_node.value = value; 31 | retval->u1.slot_access_node.index = index; 32 | return retval; 33 | } 34 | 35 | ast_node *ast_node_create_array(ast_node *elements) 36 | { 37 | ast_node *retval = malloc(sizeof(ast_node)); 38 | retval->type = AST_NODE_TYPE_ARRAY; 39 | retval->u1.array_node.elements = elements; 40 | return retval; 41 | } 42 | 43 | ast_node *ast_node_create_assert(ast_node *expression, int lineno) 44 | { 45 | 46 | ast_node *retval = malloc(sizeof(ast_node)); 47 | retval->type = AST_NODE_TYPE_ASSERT; 48 | retval->u1.assert_node.expr = expression; 49 | retval->u1.assert_node.lineno = lineno; 50 | return retval; 51 | } 52 | 53 | ast_node *ast_node_create_tyepof(ast_node *expression) 54 | { 55 | 56 | ast_node *retval = malloc(sizeof(ast_node)); 57 | retval->type = AST_NODE_TYPE_TYPEOF; 58 | retval->u1.typeof_node.expr = expression; 59 | return retval; 60 | } 61 | 62 | ast_node *ast_node_create_postfix_op(ast_postfix_op_type type, 63 | ast_node *expression) 64 | { 65 | 66 | ast_node *retval = malloc(sizeof(ast_node)); 67 | retval->type = AST_NODE_TYPE_POSTFIX; 68 | retval->u1.postfix_node.type = type; 69 | retval->u1.postfix_node.expr = expression; 70 | return retval; 71 | } 72 | 73 | ast_node *ast_node_create_unary_op(ast_unary_op_type type, ast_node *expr) 74 | { 75 | ast_node *retval = malloc(sizeof(ast_node)); 76 | retval->type = AST_NODE_TYPE_UNARY_OP; 77 | retval->u1.unary_node.type = type; 78 | retval->u1.unary_node.expr = expr; 79 | return retval; 80 | } 81 | 82 | ast_node *ast_node_create_number(long value) 83 | { 84 | ast_node *retval = malloc(sizeof(ast_node)); 85 | retval->type = AST_NODE_TYPE_NUMBER; 86 | retval->u1.number_value = value; 87 | return retval; 88 | } 89 | 90 | ast_node *ast_node_create_statement_list(size_t count, ...) 91 | { 92 | va_list va; 93 | size_t i; 94 | 95 | ast_node *retval = malloc(sizeof(ast_node)); 96 | retval->type = AST_NODE_TYPE_STATEMENT_LIST; 97 | 98 | if(count > 0) 99 | { 100 | retval->u1.statements_node.count = count; 101 | retval->u1.statements_node.capacity = count; 102 | retval->u1.statements_node.statement_list = 103 | malloc(sizeof(ast_node) * (count)); 104 | va_start(va, count); 105 | 106 | for(i = 0; i < count; i++) 107 | { 108 | retval->u1.statements_node.statement_list[i] = 109 | va_arg(va, ast_node *); 110 | } 111 | 112 | va_end(va); 113 | } 114 | else 115 | { 116 | retval->u1.statements_node.count = 0; 117 | retval->u1.statements_node.capacity = 2; 118 | retval->u1.statements_node.statement_list = 119 | malloc(sizeof(ast_node) * 2); 120 | retval->u1.statements_node.statement_list[0] = NULL; 121 | retval->u1.statements_node.statement_list[1] = NULL; 122 | } 123 | 124 | return retval; 125 | } 126 | 127 | void ast_node_statement_list_push(ast_node *node, ast_node *value) 128 | { 129 | if(node->u1.statements_node.count >= node->u1.statements_node.capacity) 130 | { 131 | node->u1.statements_node.capacity += 1; 132 | 133 | size_t new_capacity = node->u1.statements_node.capacity; 134 | 135 | node->u1.statements_node.statement_list = 136 | realloc(node->u1.statements_node.statement_list, 137 | sizeof(ast_node) * new_capacity); 138 | 139 | node->u1.statements_node.statement_list[ 140 | node->u1.statements_node.count++] = value; 141 | } 142 | else 143 | { 144 | node->u1.statements_node.statement_list[ 145 | node->u1.statements_node.count++] = value; 146 | } 147 | } 148 | 149 | ast_node *ast_node_create_binary_op(ast_binary_op_type type, ast_node *left, 150 | ast_node *right) 151 | { 152 | ast_node *retval = malloc(sizeof(ast_node)); 153 | 154 | retval->type = AST_NODE_TYPE_BIN_OP; 155 | retval->u1.binary_node.type = type; 156 | retval->u1.binary_node.left = left; 157 | retval->u1.binary_node.right = right; 158 | 159 | return retval; 160 | } 161 | 162 | ast_node *ast_node_create_id(const char *name) 163 | { 164 | ast_node *retval = malloc(sizeof(ast_node)); 165 | retval->type = AST_NODE_TYPE_ID; 166 | 167 | size_t len = strlen(name); 168 | 169 | retval->u1.id_node.length = len; 170 | retval->u1.id_node.name = malloc(len + 1); 171 | 172 | memcpy(retval->u1.id_node.name, name, len + 1); 173 | 174 | return retval; 175 | } 176 | 177 | ast_node *ast_node_create_if(ast_node *condition, ast_node *b1, ast_node *b2) 178 | { 179 | ast_node *retval = malloc(sizeof(ast_node)); 180 | 181 | retval->type = AST_NODE_TYPE_IF; 182 | retval->u1.if_node.condition = condition; 183 | retval->u1.if_node.b1 = b1; 184 | retval->u1.if_node.b2 = b2; 185 | 186 | return retval; 187 | } 188 | 189 | ast_node *ast_node_create_while(ast_node *condition, ast_node *body) 190 | { 191 | ast_node *retval = malloc(sizeof(ast_node)); 192 | 193 | retval->type = AST_NODE_TYPE_WHILE; 194 | retval->u1.while_node.condition = condition; 195 | retval->u1.while_node.body = body; 196 | 197 | return retval; 198 | } 199 | 200 | ast_node *ast_node_create_for(ast_node *initialization, 201 | ast_node *condition, ast_node *final_expression, ast_node *body) 202 | { 203 | ast_node *retval = malloc(sizeof(ast_node)); 204 | 205 | retval->type = AST_NODE_TYPE_FOR; 206 | retval->u1.for_node.initialization = initialization; 207 | retval->u1.for_node.condition = condition; 208 | retval->u1.for_node.final_expression = final_expression; 209 | retval->u1.for_node.body = body; 210 | 211 | return retval; 212 | } 213 | 214 | ast_node *ast_node_create_function(const char *name, ast_node *parameters, 215 | ast_node *body) 216 | { 217 | ast_node *retval = malloc(sizeof(ast_node)); 218 | 219 | retval->type = AST_NODE_TYPE_FUNC_DECL; 220 | 221 | size_t len = strlen(name); 222 | 223 | retval->u1.function_node.name_length = len; 224 | retval->u1.function_node.name = malloc(len + 1); 225 | 226 | memcpy(retval->u1.function_node.name, name, len + 1); 227 | 228 | retval->u1.function_node.parameter_list = parameters; 229 | retval->u1.function_node.body = body; 230 | 231 | return retval; 232 | } 233 | 234 | ast_node *ast_node_create_call(ast_node *id, ast_node *args, int lineno, 235 | int col) 236 | { 237 | ast_node *retval = malloc(sizeof(ast_node)); 238 | 239 | retval->type = AST_NODE_TYPE_CALL; 240 | retval->u1.call_node.id = id; 241 | retval->u1.call_node.arguments = args; 242 | retval->u1.call_node.lineno = lineno; 243 | retval->u1.call_node.colno = col; 244 | 245 | return retval; 246 | } 247 | 248 | ast_node *ast_node_create_return(ast_node *expr) 249 | { 250 | ast_node *retval = malloc(sizeof(ast_node)); 251 | 252 | retval->type = AST_NODE_TYPE_RET; 253 | retval->u1.return_node.expr = expr; 254 | 255 | return retval; 256 | } 257 | 258 | 259 | ast_node *ast_node_create_print(ast_node *expr) 260 | { 261 | ast_node *retval = malloc(sizeof(ast_node)); 262 | 263 | retval->type = AST_NODE_TYPE_PRINT; 264 | retval->u1.print_node.expr = expr; 265 | 266 | return retval; 267 | } 268 | 269 | ast_node *ast_node_create_string_literal(const char *str) 270 | { 271 | ast_node *retval = malloc(sizeof(ast_node)); 272 | 273 | retval->type = AST_NODE_TYPE_STRING; 274 | retval->u1.string_value.length = strlen(str); 275 | retval->u1.string_value.value = malloc(retval->u1.string_value.length + 1); 276 | 277 | memcpy(retval->u1.string_value.value, str, retval->u1.string_value.length); 278 | 279 | retval->u1.string_value.value[retval->u1.string_value.length] = '\0'; 280 | 281 | return retval; 282 | } 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | -------------------------------------------------------------------------------- /vm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define COMO_COMPILER 1 8 | 9 | #include "como_ast.h" 10 | #include "como_stack.h" 11 | #include "como_debug.h" 12 | #include "como_opcode.h" 13 | #include "como_globals.h" 14 | #include "como_parser.h" 15 | #include "como_lexer.h" 16 | #include "como_compiler.h" 17 | #include "como_io.h" 18 | #include "como_object.h" 19 | 20 | static ComoFrame *global_frame = NULL; 21 | 22 | static ComoOpCode *create_op(unsigned char op, Object *oper) 23 | { 24 | ComoOpCode *ret = malloc(sizeof(ComoOpCode)); 25 | ret->op_code = op; 26 | 27 | ret->flags = 0; 28 | 29 | if(oper != NULL) { 30 | ret->flags |= COMO_OP_CODE_OPERAND_USED; 31 | } 32 | 33 | // TODO, line numbers 34 | 35 | ret->operand = oper; 36 | return ret; 37 | } 38 | 39 | static ComoFrame *create_frame(Object *code, const char *name) 40 | { 41 | size_t i; 42 | ComoFrame *frame = malloc(sizeof(ComoFrame)); 43 | 44 | frame->cf_sp = 0; 45 | frame->cf_stack_size = 0; 46 | 47 | for(i = 0; i < (size_t)COMO_DEFAULT_FRAME_STACKSIZE; i++) { 48 | frame->cf_stack[i] = NULL; 49 | } 50 | 51 | frame->cf_symtab = newMap(4); 52 | frame->code = code; 53 | frame->pc = 0; 54 | frame->next = NULL; 55 | frame->namedparameters = newArray(2); 56 | frame->filename = NULL; 57 | frame->call_stack = NULL; 58 | frame->name = newString(name); 59 | frame->caller = NULL; 60 | return frame; 61 | } 62 | 63 | static void print_object(Object *obj) 64 | { 65 | if(Obj_CheckExact(obj, IS_STRING)) { 66 | fprintf(stdout, "%s\n", O_SVAL(obj)->value); 67 | } else { 68 | char *str = objectToString(obj); 69 | fprintf(stdout, "%s\n", str); 70 | free(str); 71 | } 72 | } 73 | 74 | 75 | static int exception = 0; 76 | static char exmessage[1024]; 77 | 78 | #define Como_SetError(fmt, ...) do { \ 79 | exception = 1; \ 80 | memset(exmessage, 0, sizeof(exmessage)); \ 81 | sprintf(exmessage, fmt, __VA_ARGS__); \ 82 | } while(0) 83 | 84 | 85 | 86 | static void Como_EnterFrameDebug(ComoFrame *frame) 87 | { 88 | fprintf(stderr, " ---\n"); 89 | fprintf(stderr, " --- Frame: %s at <%p>\n", 90 | O_SVAL(frame->name)->value, (void *)frame); 91 | 92 | fprintf(stderr, " --- Stack at <%p>\n", (void *)frame->cf_stack); 93 | fprintf(stderr, " --- Symtab at <%p>\n", (void *)frame->cf_symtab); 94 | fprintf(stderr, " ---\n"); 95 | } 96 | 97 | /* VM main loop */ 98 | Object *Como_EvalFrame(ComoFrame *frame) 99 | { 100 | #define O_INCRREF(O) \ 101 | O_REFCNT((O))++ 102 | 103 | #define O_DECREF(O) do { \ 104 | if(--O_REFCNT((O)) == 0) { \ 105 | objectDestroy((O)); \ 106 | } \ 107 | } while(0) 108 | 109 | #define OPR_DECREF(Opcode) do { \ 110 | (Opcode)->flags |= COMO_OP_CODE_OPERAND_FREE; \ 111 | O_DECREF((Opcode)->operand); \ 112 | (Opcode)->operand = NULL; \ 113 | } while(0) 114 | 115 | #define TARGET(Name) \ 116 | case Name: 117 | 118 | #define VM_CONTINUE() \ 119 | goto next_opcode 120 | 121 | #define VM_DISPATCH() \ 122 | break 123 | 124 | #define NEXT_OPCODE() \ 125 | (ComoOpCode *)O_PTVAL(O_AVAL((frame)->code)->table[(frame)->pc++]); 126 | 127 | #define POP() \ 128 | frame->cf_stack[--frame->cf_sp] 129 | 130 | #define PUSH(Obj) \ 131 | frame->cf_stack[frame->cf_sp++] = Obj 132 | 133 | #define OP1() \ 134 | opcode->operand 135 | 136 | #define EMPTY() (frame->cf_sp == 0) 137 | 138 | Object *retval = NULL; 139 | 140 | for(;;) 141 | { 142 | ComoOpCode *opcode = NEXT_OPCODE(); 143 | 144 | #ifdef COMO_DEBUG 145 | fprintf(stderr, "%s\n", instrstr(opcode)); 146 | #endif 147 | 148 | switch(opcode->op_code) 149 | { 150 | TARGET(HALT) 151 | { 152 | goto exit; 153 | } 154 | 155 | TARGET(IPRINT) 156 | { 157 | Object *value = POP(); 158 | 159 | print_object(value); 160 | 161 | O_DECREF(value); 162 | 163 | VM_DISPATCH(); 164 | } 165 | 166 | TARGET(LOAD_NAME) 167 | { 168 | Object *arg = OP1(); 169 | Object *value = 170 | mapSearchEx(frame->cf_symtab, O_SVAL(arg)->value); 171 | 172 | if(value == NULL) 173 | { 174 | Como_SetError( 175 | "Undefined symbol, %s", O_SVAL(arg)->value 176 | ); 177 | } 178 | else 179 | { 180 | PUSH(value); 181 | } 182 | 183 | OPR_DECREF(opcode); 184 | 185 | VM_DISPATCH(); 186 | } 187 | 188 | TARGET(LOAD_CONST) 189 | { 190 | Object *arg = OP1(); 191 | PUSH(arg); 192 | 193 | O_INCRREF(arg); 194 | 195 | VM_DISPATCH(); 196 | } 197 | 198 | /* STORE_NAME "foo" */ 199 | TARGET(STORE_NAME) 200 | { 201 | Object *value = POP(); 202 | Object *name = OP1(); 203 | 204 | mapInsertEx(frame->cf_symtab, O_SVAL(name)->value, value); 205 | 206 | O_INCRREF(value); 207 | 208 | OPR_DECREF(opcode); 209 | 210 | VM_DISPATCH(); 211 | } 212 | TARGET(IADD) 213 | { 214 | Object *right = POP(); 215 | Object *left = POP(); 216 | 217 | if(Obj_CheckExact(right, IS_LONG) && 218 | Obj_CheckExact(left, IS_LONG)) 219 | { 220 | long result = O_LVAL(left) + O_LVAL(right); 221 | 222 | PUSH(newLong(result)); 223 | } 224 | else 225 | { 226 | /* Temp char buffers */ 227 | char *_s1 = objectToString(left); 228 | char *_s2 = objectToString(right); 229 | 230 | Object *s1 = newString(_s1); 231 | Object *s2 = newString(_s2); 232 | 233 | Object *result = stringCat(s1, s2); 234 | 235 | free(_s1); 236 | free(_s2); 237 | 238 | O_DECREF(s1); 239 | O_DECREF(s2); 240 | 241 | PUSH(result); 242 | } 243 | 244 | 245 | O_DECREF(left); 246 | O_DECREF(right); 247 | 248 | VM_DISPATCH(); 249 | } 250 | } 251 | 252 | if(exception) { 253 | como_error_noreturn(frame, exmessage); 254 | } 255 | } 256 | 257 | exit: 258 | /* Pop remaining stack entries */ 259 | while(!EMPTY()) { 260 | Object *obj = POP(); 261 | object_print_stats(obj); 262 | O_DECREF(obj); 263 | } 264 | 265 | return retval; 266 | } 267 | 268 | 269 | static void destroy_frame(ComoFrame *frame) 270 | { 271 | uint32_t i; 272 | 273 | objectDestroy(frame->lineno); 274 | objectDestroy(frame->filename); 275 | 276 | for(i = 0; i < mapCapacity(frame->cf_symtab); i++) { 277 | Bucket *b = mapGetBucket(frame->cf_symtab, i); 278 | 279 | while(b != NULL) { 280 | Bucket *bn = b->next; 281 | String *key = b->key; 282 | free(key->value); 283 | free(key); 284 | free(b); 285 | b = bn; 286 | } 287 | } 288 | 289 | free(O_MVAL(frame->cf_symtab)->buckets); 290 | free(O_MVAL(frame->cf_symtab)); 291 | free(frame->cf_symtab); 292 | 293 | objectDestroy(frame->namedparameters); 294 | objectDestroy(frame->name); 295 | 296 | for(i = 0; i < O_AVAL(frame->code)->size; i++) 297 | { 298 | ComoOpCode *op = (ComoOpCode *)O_PTVAL((O_AVAL(frame->code))->table[i]); 299 | 300 | if((op->flags & COMO_OP_CODE_OPERAND_USED) && 301 | !(op->flags & COMO_OP_CODE_OPERAND_FREE)) 302 | { 303 | objectDestroy(op->operand); 304 | } 305 | 306 | free(op); 307 | } 308 | 309 | objectDestroy(frame->code); 310 | 311 | fprintf(stdout, " --- Stack Pointer: %d\n", (int)frame->cf_sp); 312 | fprintf(stdout, " --- Program Counter: %d\n", (int)frame->pc); 313 | 314 | free(frame); 315 | } 316 | 317 | static void _como_compile_ast(ast_node *p, const char *filename, int dump_asm) { 318 | Object *main_code = newArray(4); 319 | 320 | global_frame = create_frame(main_code, "__main__"); 321 | 322 | global_frame->lineno = newLong(0L); 323 | global_frame->filename = newString(filename); 324 | 325 | como_compile(p, global_frame); 326 | 327 | arrayPushEx(main_code, newPointer((void *)create_op(HALT, NULL))); 328 | 329 | if(!dump_asm) 330 | (void)Como_EvalFrame(global_frame); 331 | 332 | destroy_frame(global_frame); 333 | } 334 | 335 | 336 | void como_compile_ast(ast_node *p, const char *filename) { 337 | _como_compile_ast(p, filename, 0); 338 | } 339 | 340 | 341 | char *get_active_file_name(void) { 342 | return "-"; 343 | } 344 | -------------------------------------------------------------------------------- /vm/vm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "como_opcode.h" 10 | 11 | #define OP(i) ((i) >> 24 & 0xff) 12 | #define OP1(i) ((i) >> 8 & 0xffff) 13 | #define FLAG(i) ((i) & 0xff) 14 | 15 | typedef struct Frame { 16 | Object *name; /* String */ 17 | Object *code; /* Array of Long */ 18 | Object *pc; /* Long */ 19 | Object *constants; /* Array */ 20 | Object *locals; /* Map */ 21 | Object *stack; /* Array */ 22 | Object *exception; 23 | long sp; /* Index into stack, UNSAFE */ 24 | struct Frame *parent; 25 | } Frame; 26 | 27 | #define CONSTANT_PTR(frame) \ 28 | ((O_AVAL((frame)->constants)->size) - 1) 29 | 30 | #define add_obj_constant(frame, object) \ 31 | arrayPushEx((frame)->constants, (object)) 32 | 33 | #define add_int_constant(frame, ival) \ 34 | add_obj_constant((frame), newLong((long)(ival))) 35 | 36 | #define add_string_constant(frame, sval) \ 37 | add_obj_constant((frame), newString((sval))) 38 | 39 | #define add_double_constant(frame, dval) \ 40 | add_obj_constant((frame), newDouble((dval))) 41 | 42 | #define add_local(frame, key, value) \ 43 | mapInsertEx((frame)->locals, (key), (value)) 44 | 45 | static Frame *GLOBAL_FRAME; 46 | 47 | static void init_frame(char *name, Frame *frame, Frame *parent) 48 | { 49 | frame->name = newString(name); 50 | frame->code = newArray(8); 51 | frame->pc = newLong(0L); 52 | frame->constants = newArray(8); 53 | frame->locals = newMap(8); 54 | frame->stack = newArray(8); 55 | frame->exception = NULL; 56 | frame->sp = 0; 57 | frame->parent = parent; 58 | } 59 | 60 | #define PACK_INSTRUCTION(opcode, argument, flag) \ 61 | ((((uint8_t)(opcode)) << 24) | (((uint16_t)(argument)) << 8) | ((uint8_t)(flag))) 62 | 63 | #define emit(frame, opcode, argument, flag) \ 64 | arrayPushEx((frame)->code, newLong( \ 65 | (long)PACK_INSTRUCTION( \ 66 | (opcode), (argument), (flag) \ 67 | ) \ 68 | ) \ 69 | ) \ 70 | 71 | static Frame *create_add_function() 72 | { 73 | Frame *frame = malloc(sizeof(*frame)); 74 | 75 | init_frame("add", frame, GLOBAL_FRAME); 76 | 77 | emit(frame, IADD, 0, 0); 78 | emit(frame, IRETURN, 0, 0); 79 | 80 | return frame; 81 | } 82 | 83 | #define Object_TypeCheck(obj, type) \ 84 | (O_TYPE((obj)) == (type)) 85 | 86 | #define Would_Overflow(a, b) \ 87 | (((b > 0L) && (a > LONG_MAX - b)) || ((b < 0L) && (a < LONG_MIN - b))) 88 | 89 | static Object *do_add(Object *ob1, Object *ob2) 90 | { 91 | if(Object_TypeCheck(ob1, IS_STRING) && 92 | Object_TypeCheck(ob2, IS_STRING)) 93 | { 94 | return stringCat(ob1, ob2); 95 | } 96 | else if(Object_TypeCheck(ob1, IS_LONG) && 97 | Object_TypeCheck(ob2, IS_LONG)) 98 | { 99 | 100 | if(Would_Overflow(O_LVAL(ob1), O_LVAL(ob2))) 101 | { 102 | fprintf(stderr, "%s\n", 103 | "warning: evalulation of expression " 104 | "would overflow"); 105 | 106 | return newLong(0L); 107 | } 108 | else 109 | { 110 | return newLong(O_LVAL(ob1) + O_LVAL(ob2)); 111 | } 112 | } 113 | else if 114 | ((Object_TypeCheck(ob1, IS_DOUBLE) || Object_TypeCheck(ob1, IS_LONG)) 115 | && ((Object_TypeCheck(ob2, IS_DOUBLE) || 116 | Object_TypeCheck(ob2, IS_LONG)))) 117 | { 118 | double left, right; 119 | 120 | if(Object_TypeCheck(ob1, IS_DOUBLE)) 121 | { 122 | left = O_DVAL(ob1); 123 | } 124 | else 125 | { 126 | left = (double)O_LVAL(ob1); 127 | } 128 | 129 | if(Object_TypeCheck(ob2, IS_DOUBLE)) 130 | { 131 | right = O_DVAL(ob2); 132 | } 133 | else 134 | { 135 | right = (double)O_LVAL(ob2); 136 | } 137 | 138 | return newDouble(left + right); 139 | } 140 | else 141 | { 142 | /* TODO Set exception */ 143 | return NULL; 144 | } 145 | } 146 | 147 | static Object *Como_EvalFrameEx(Frame *frame) 148 | { 149 | 150 | #define VM_CASE(o) switch(o) 151 | 152 | #define VM_TARGET(x) case x: 153 | 154 | #define VM_BREAK() break 155 | 156 | #define FETCH() \ 157 | ((int)(((((uint32_t)( \ 158 | O_LVAL( \ 159 | O_AVAL((frame)->code) \ 160 | ->table[O_LVAL(frame->pc)++] \ 161 | ) \ 162 | )) >> 24) & 0xff))) \ 163 | 164 | #define ARG() \ 165 | (((uint32_t)O_LVAL(O_AVAL((frame)->code)->table[((int)O_LVAL(frame->pc))-1])) >> 8 & 0xffff) 166 | 167 | #define CONSTANT(indi) \ 168 | O_AVAL(frame->constants)->table[(indi)] 169 | 170 | #define PUSH(o) \ 171 | O_AVAL(frame->stack)->table[frame->sp++] = o 172 | 173 | #define POP() \ 174 | (O_AVAL(frame->stack)->table[--frame->sp]) 175 | 176 | #define Set_Exception(frame, message) \ 177 | frame->exception = newString(message) 178 | 179 | #define EMPTY() \ 180 | (frame->sp == 0) 181 | 182 | Object *left, *right, *result, *value, *retval = NULL; 183 | uint32_t arg = -1; 184 | 185 | for(;;) 186 | { 187 | VM_CASE(FETCH()) 188 | { 189 | VM_TARGET(IPRINT) 190 | { 191 | value = POP(); 192 | 193 | objectEcho(value); 194 | 195 | VM_BREAK(); 196 | } 197 | 198 | VM_TARGET(LOAD_CONST) 199 | { 200 | PUSH(CONSTANT(ARG())); 201 | 202 | VM_BREAK(); 203 | } 204 | VM_TARGET(LOAD_NAME) 205 | { 206 | value = CONSTANT(ARG()); 207 | 208 | result = mapSearchEx(frame->locals, O_SVAL(value)->value); 209 | 210 | if(result == NULL) 211 | { 212 | Set_Exception(frame, "undefined symbol"); 213 | } 214 | else 215 | { 216 | PUSH(result); 217 | } 218 | 219 | VM_BREAK(); 220 | } 221 | VM_TARGET(CALL_FUNCTION) 222 | { 223 | value = POP(); 224 | arg = ARG(); 225 | int i = arg; 226 | 227 | if(!Object_TypeCheck(value, IS_POINTER)) 228 | { 229 | Set_Exception(frame, "cannot call a non callable value"); 230 | } 231 | else 232 | { 233 | Frame *this_function = (Frame *)O_PTVAL(value); 234 | Frame *old_frame = frame; 235 | 236 | /* Setup the function stack 237 | * TODO when there are named locals (parameters), 238 | make sure to swap them in 239 | */ 240 | while(i--) 241 | { 242 | /* As you can see this is a hack, as PUSH, POP 243 | cannot have the frame specified 244 | */ 245 | Object *_ = POP(); 246 | frame = this_function; 247 | PUSH(_); 248 | frame = old_frame; 249 | } 250 | 251 | result = Como_EvalFrameEx(this_function); 252 | 253 | PUSH(result); 254 | } 255 | 256 | VM_BREAK(); 257 | } 258 | VM_TARGET(IADD) 259 | { 260 | right = POP(); 261 | left = POP(); 262 | 263 | result = do_add(left, right); 264 | 265 | if(result == NULL) 266 | Set_Exception(frame, "unsupported operand types for +"); 267 | else 268 | PUSH(result); 269 | 270 | VM_BREAK(); 271 | } 272 | VM_TARGET(IRETURN) 273 | { 274 | if(!EMPTY()) 275 | retval = POP(); 276 | 277 | goto done; 278 | 279 | VM_BREAK(); 280 | } 281 | VM_TARGET(HALT) 282 | { 283 | goto done; 284 | } 285 | } 286 | 287 | if(frame->exception != NULL) 288 | { 289 | fprintf(stderr, "Exception: %s\n", O_SVAL(frame->exception)->value); 290 | goto done; 291 | } 292 | } 293 | 294 | done: 295 | return retval; 296 | } 297 | 298 | static void dump_config() 299 | { 300 | fprintf(stderr, "LONG_MAX=%ld\n", LONG_MAX); 301 | } 302 | 303 | int main(void) 304 | { 305 | dump_config(); 306 | 307 | struct Frame frame; 308 | 309 | init_frame("main", &frame, NULL); 310 | 311 | GLOBAL_FRAME = &frame; 312 | 313 | Frame *add_function = create_add_function(); 314 | add_local(&frame, "add", newPointer(add_function)); 315 | 316 | add_int_constant(&frame, 1); 317 | emit(&frame, LOAD_CONST, CONSTANT_PTR(&frame), 0); 318 | 319 | add_int_constant(&frame, LONG_MAX); 320 | emit(&frame, LOAD_CONST, CONSTANT_PTR(&frame), 0); 321 | 322 | add_string_constant(&frame, "add"); 323 | emit(&frame, LOAD_NAME, CONSTANT_PTR(&frame), 0); 324 | 325 | emit(&frame, CALL_FUNCTION, 2, 0); 326 | 327 | emit(&frame, IPRINT, 0, 0); 328 | 329 | emit(&frame, IRETURN, 0, 0); 330 | emit(&frame, HALT, 0, 0); 331 | 332 | Object *retval = Como_EvalFrameEx(&frame); 333 | 334 | if(retval) 335 | OBJECT_DUMP(retval); 336 | 337 | return 0; 338 | } 339 | -------------------------------------------------------------------------------- /como_parser.y: -------------------------------------------------------------------------------- 1 | %{ 2 | /* 3 | * Copyright (c) 2016 Ryan McCullagh 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | */ 18 | 19 | #include "como_globals.h" 20 | #include "como_ast.h" 21 | #include "como_parser.h" 22 | #include "como_lexer.h" 23 | #include "como_io.h" 24 | 25 | #define YYERROR_VERBOSE 26 | 27 | int yyerror(YYLTYPE * lvalp, ast_node** ast, yyscan_t scanner, const char* msg) 28 | { 29 | printf("parse error: %s in file \"%s\" on line %d:%d\n", msg, 30 | get_active_file_name(), lvalp->first_line, lvalp->first_column); 31 | 32 | exit(1); 33 | } 34 | 35 | %} 36 | 37 | %code requires { 38 | 39 | extern ast_node *como_parse(const char *filename); 40 | 41 | 42 | #ifndef YY_TYPEDEF_YY_SCANNER_T 43 | #define YY_TYPEDEF_YY_SCANNER_T 44 | typedef void* yyscan_t; 45 | #endif 46 | 47 | } 48 | 49 | %defines "como_parser.h" 50 | 51 | %pure-parser 52 | %locations 53 | %error-verbose 54 | %expect 0 55 | %lex-param { yyscan_t scanner } 56 | %parse-param { ast_node** ast } 57 | %parse-param { yyscan_t scanner } 58 | 59 | %union { 60 | long number; 61 | char* id; 62 | char* stringliteral; 63 | ast_node* ast; 64 | } 65 | 66 | %left T_CMP 67 | %left T_LTE 68 | %left T_AND 69 | %left T_NEQ 70 | %precedence T_TYPEOF 71 | %left T_GTE 72 | %left '<' 73 | %left '>' 74 | %left '-' 75 | %left '+' 76 | %left '*' 77 | %left '/' 78 | %left '%' 79 | %precedence '!' 80 | 81 | %token END 0 "EOF" 82 | %token '-' 83 | %token '+' 84 | %token '*' 85 | %token '/' 86 | %token '<' 87 | %token '>' 88 | %token '!' 89 | 90 | %token T_IF 91 | %token T_LTE 92 | %token T_AND 93 | %token T_ELSE 94 | %token T_WHILE 95 | %token T_FOR 96 | %token T_FUNC 97 | %token T_RETURN 98 | %token T_CMP 99 | %token T_PRINT 100 | %token T_NOELSE 101 | %token T_NEQ 102 | %token T_GTE 103 | %token T_INC 104 | %token T_DEC 105 | %token T_FUNCTION 106 | %token T_TYPEOF 107 | %token T_ASSERT 108 | 109 | %token T_NUM 110 | %token T_ID 111 | %token T_STR_LIT 112 | 113 | %type top_statement statement expression_statement compound_statement selection_statement 114 | %type inner_statement if_statement_without_else 115 | %type expr 116 | %type top_statement_list inner_statement_list 117 | %type optional_parameter_list parameter_list 118 | %type parameter 119 | %type function_decl_statement 120 | %type optional_argument_list argument_list argument 121 | %type return_statement optional_expression 122 | %type assignment_statement print_statement assert_statement primary_expression 123 | 124 | %% 125 | 126 | start: 127 | top_statement_list { *ast = $1; } 128 | ; 129 | 130 | top_statement_list: 131 | top_statement_list top_statement { ast_node_statement_list_push($1, $2); $$ = $1; } 132 | | %empty { $$ = ast_node_create_statement_list(0); } 133 | ; 134 | 135 | top_statement: 136 | statement { $$ = $1; } 137 | ; 138 | 139 | inner_statement_list: 140 | inner_statement_list inner_statement { ast_node_statement_list_push($1, $2); $$ = $1; } 141 | | %empty { $$ = ast_node_create_statement_list(0); } 142 | ; 143 | 144 | inner_statement: 145 | statement { $$ = $1; } 146 | ; 147 | 148 | function_keyword: 149 | T_FUNC | T_FUNCTION 150 | ; 151 | 152 | statement: 153 | function_decl_statement { $$ = $1; } 154 | | 155 | compound_statement { $$ = $1; } 156 | | 157 | expression_statement { $$ = $1; } 158 | | 159 | selection_statement { $$ = $1; } 160 | | 161 | return_statement { $$ = $1; } 162 | ; 163 | 164 | assignment_statement: 165 | T_ID '=' primary_expression { 166 | $$ = ast_node_create_binary_op(AST_BINARY_OP_ASSIGN, ast_node_create_id($1), $3); 167 | free($1); 168 | } 169 | ; 170 | 171 | print_statement: 172 | T_PRINT '(' primary_expression ')' { 173 | $$ = ast_node_create_print($3); 174 | } 175 | ; 176 | 177 | assert_statement: 178 | T_ASSERT '(' primary_expression ')' { 179 | $$ = ast_node_create_assert($3, @1.first_line); 180 | } 181 | ; 182 | 183 | return_statement: 184 | T_RETURN optional_expression ';' { $$ = ast_node_create_return($2); } 185 | ; 186 | 187 | optional_expression: 188 | primary_expression { $$ = $1; } 189 | | %empty { $$ = NULL; } 190 | ; 191 | 192 | compound_statement: 193 | '{' inner_statement_list '}' { $$ = $2; } 194 | ; 195 | 196 | expression_statement: 197 | assignment_statement ';' { $$ = $1; } 198 | | 199 | print_statement ';' { $$ = $1; } 200 | | 201 | assert_statement ';' { $$ = $1; } 202 | | 203 | primary_expression ';' { 204 | $$ = $1; 205 | } 206 | ; 207 | 208 | primary_expression: 209 | expr { 210 | $$ = $1; 211 | } 212 | | 213 | primary_expression '[' primary_expression ']' { 214 | $$ = ast_node_create_slot_access($1, $3); 215 | } 216 | ; 217 | 218 | 219 | if_statement_without_else: 220 | T_IF '(' primary_expression ')' compound_statement { $$ = ast_node_create_if($3, $5, NULL); } 221 | ; 222 | 223 | selection_statement: 224 | if_statement_without_else %prec T_NOELSE { $$ = $1; } 225 | | 226 | if_statement_without_else T_ELSE compound_statement { $1->u1.if_node.b2 = $3; $$ = $1; } 227 | | 228 | T_WHILE '(' primary_expression ')' compound_statement { 229 | $$ = ast_node_create_while($3, $5); 230 | } 231 | | 232 | T_FOR '(' assignment_statement ';' primary_expression ';' primary_expression ')' compound_statement { 233 | $$ = ast_node_create_for($3, $5, $7, $9); 234 | } 235 | ; 236 | 237 | function_decl_statement: 238 | function_keyword T_ID '('optional_parameter_list')' compound_statement { 239 | $$ = ast_node_create_function($2, $4, $6); 240 | free($2); 241 | } 242 | ; 243 | 244 | optional_parameter_list: 245 | parameter_list { $$ = $1; } 246 | | 247 | %empty { $$ = ast_node_create_statement_list(0); } 248 | ; 249 | 250 | parameter_list: 251 | parameter { $$ = ast_node_create_statement_list(1, $1); } 252 | | 253 | parameter_list ',' parameter { ast_node_statement_list_push($1, $3); $$ = $1; } 254 | ; 255 | 256 | parameter: 257 | T_ID { $$ = ast_node_create_id($1); free($1); } 258 | ; 259 | 260 | optional_argument_list: 261 | argument_list { $$ = $1; } 262 | | 263 | %empty { $$ = ast_node_create_statement_list(0); } 264 | ; 265 | 266 | argument_list: 267 | argument { $$ = ast_node_create_statement_list(1, $1); } 268 | | 269 | argument_list ',' argument { ast_node_statement_list_push($1, $3); $$ = $1; } 270 | ; 271 | 272 | argument: 273 | primary_expression { $$ = $1; } 274 | ; 275 | 276 | 277 | expr: 278 | expr '+' expr { $$ = ast_node_create_binary_op(AST_BINARY_OP_ADD, $1, $3); } 279 | | 280 | expr '-' expr { $$ = ast_node_create_binary_op(AST_BINARY_OP_MINUS, $1, $3); } 281 | | 282 | expr '*' expr { $$ = ast_node_create_binary_op(AST_BINARY_OP_TIMES, $1, $3); } 283 | | 284 | expr '/' expr { $$ = ast_node_create_binary_op(AST_BINARY_OP_DIV, $1, $3); } 285 | | 286 | '(' expr ')' { $$ = $2; } 287 | | 288 | expr '<' expr { 289 | $$ = ast_node_create_binary_op(AST_BINARY_OP_LT, $1, $3); 290 | } 291 | | 292 | expr '>' expr { 293 | $$ = ast_node_create_binary_op(AST_BINARY_OP_GT, $1, $3); 294 | } 295 | | 296 | expr '%' expr { 297 | $$ = ast_node_create_binary_op(AST_BINARY_OP_REM, $1, $3); 298 | } 299 | | 300 | expr T_CMP expr { 301 | $$ = ast_node_create_binary_op(AST_BINARY_OP_CMP, $1, $3); 302 | } 303 | | 304 | expr T_AND expr { 305 | $$ = ast_node_create_binary_op(AST_BINARY_OP_AND, $1, $3); 306 | } 307 | | 308 | expr T_NEQ expr { 309 | $$ = ast_node_create_binary_op(AST_BINARY_OP_NEQ, $1, $3); 310 | } 311 | | 312 | expr T_LTE expr { 313 | $$ = ast_node_create_binary_op(AST_BINARY_OP_LTE, $1, $3); 314 | } 315 | | 316 | expr T_GTE expr { 317 | $$ = ast_node_create_binary_op(AST_BINARY_OP_GTE, $1, $3); 318 | } 319 | | 320 | T_ID T_INC { 321 | $$ =ast_node_create_postfix_op(AST_POSTFIX_OP_INC, ast_node_create_id($1)); 322 | free($1); 323 | } 324 | | 325 | T_ID T_DEC { 326 | $$ =ast_node_create_postfix_op(AST_POSTFIX_OP_DEC, ast_node_create_id($1)); 327 | free($1); 328 | } 329 | | 330 | T_ID '(' optional_argument_list ')' { 331 | $$ = ast_node_create_call(ast_node_create_id($1), $3, @1.first_line, @1.first_column); 332 | free($1); 333 | } 334 | | 335 | '-' expr { 336 | $$ = ast_node_create_unary_op(AST_UNARY_OP_MINUS, $2); 337 | } 338 | | 339 | '!' expr { 340 | $$ = ast_node_create_unary_op(AST_UNARY_NOT, $2); 341 | } 342 | | 343 | '[' optional_argument_list ']' { 344 | $$ = ast_node_create_array($2); 345 | } 346 | | 347 | T_NUM { $$ = ast_node_create_number($1); } 348 | | 349 | T_ID { $$ = ast_node_create_id($1); free($1); } 350 | | 351 | T_STR_LIT { $$ = ast_node_create_string_literal($1); free($1); } 352 | | 353 | T_TYPEOF expr { 354 | $$ =ast_node_create_tyepof($2); 355 | } 356 | ; 357 | 358 | %% 359 | 360 | ast_node *como_parse(const char *filename) 361 | { 362 | ast_node* statements; 363 | yyscan_t scanner; 364 | YY_BUFFER_STATE state; 365 | char* text; 366 | 367 | text = como_read_file(filename); 368 | 369 | if(text == NULL) 370 | { 371 | goto fail; 372 | } 373 | 374 | if(yylex_init(&scanner)) 375 | { 376 | goto fail; 377 | } 378 | 379 | state = yy_scan_string(text, scanner); 380 | 381 | if(yyparse(&statements, scanner)) 382 | { 383 | goto fail; 384 | } 385 | 386 | yy_delete_buffer(state, scanner); 387 | 388 | yylex_destroy(scanner); 389 | 390 | free(text); 391 | 392 | return statements; 393 | 394 | fail: 395 | fprintf(stdout, "Error parsing file '%s'\n", filename); 396 | fflush(stdout); 397 | exit(1); 398 | } 399 | 400 | 401 | -------------------------------------------------------------------------------- /como_lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef yyHEADER_H 2 | #define yyHEADER_H 1 3 | #define yyIN_HEADER 1 4 | 5 | #line 6 "como_lexer.h" 6 | 7 | #line 8 "como_lexer.h" 8 | 9 | #define YY_INT_ALIGNED short int 10 | 11 | /* A lexical scanner generated by flex */ 12 | 13 | #define FLEX_SCANNER 14 | #define YY_FLEX_MAJOR_VERSION 2 15 | #define YY_FLEX_MINOR_VERSION 6 16 | #define YY_FLEX_SUBMINOR_VERSION 1 17 | #if YY_FLEX_SUBMINOR_VERSION > 0 18 | #define FLEX_BETA 19 | #endif 20 | 21 | /* First, we deal with platform-specific or compiler-specific issues. */ 22 | 23 | /* begin standard C headers. */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* end standard C headers. */ 30 | 31 | /* flex integer type definitions */ 32 | 33 | #ifndef FLEXINT_H 34 | #define FLEXINT_H 35 | 36 | /* C99 systems have . Non-C99 systems may or may not. */ 37 | 38 | #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 39 | 40 | /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, 41 | * if you want the limit (max/min) macros for int types. 42 | */ 43 | #ifndef __STDC_LIMIT_MACROS 44 | #define __STDC_LIMIT_MACROS 1 45 | #endif 46 | 47 | #include 48 | typedef int8_t flex_int8_t; 49 | typedef uint8_t flex_uint8_t; 50 | typedef int16_t flex_int16_t; 51 | typedef uint16_t flex_uint16_t; 52 | typedef int32_t flex_int32_t; 53 | typedef uint32_t flex_uint32_t; 54 | #else 55 | typedef signed char flex_int8_t; 56 | typedef short int flex_int16_t; 57 | typedef int flex_int32_t; 58 | typedef unsigned char flex_uint8_t; 59 | typedef unsigned short int flex_uint16_t; 60 | typedef unsigned int flex_uint32_t; 61 | 62 | /* Limits of integral types. */ 63 | #ifndef INT8_MIN 64 | #define INT8_MIN (-128) 65 | #endif 66 | #ifndef INT16_MIN 67 | #define INT16_MIN (-32767-1) 68 | #endif 69 | #ifndef INT32_MIN 70 | #define INT32_MIN (-2147483647-1) 71 | #endif 72 | #ifndef INT8_MAX 73 | #define INT8_MAX (127) 74 | #endif 75 | #ifndef INT16_MAX 76 | #define INT16_MAX (32767) 77 | #endif 78 | #ifndef INT32_MAX 79 | #define INT32_MAX (2147483647) 80 | #endif 81 | #ifndef UINT8_MAX 82 | #define UINT8_MAX (255U) 83 | #endif 84 | #ifndef UINT16_MAX 85 | #define UINT16_MAX (65535U) 86 | #endif 87 | #ifndef UINT32_MAX 88 | #define UINT32_MAX (4294967295U) 89 | #endif 90 | 91 | #endif /* ! C99 */ 92 | 93 | #endif /* ! FLEXINT_H */ 94 | 95 | /* TODO: this is always defined, so inline it */ 96 | #define yyconst const 97 | 98 | #if defined(__GNUC__) && __GNUC__ >= 3 99 | #define yynoreturn __attribute__((__noreturn__)) 100 | #else 101 | #define yynoreturn 102 | #endif 103 | 104 | /* An opaque pointer. */ 105 | #ifndef YY_TYPEDEF_YY_SCANNER_T 106 | #define YY_TYPEDEF_YY_SCANNER_T 107 | typedef void* yyscan_t; 108 | #endif 109 | 110 | /* For convenience, these vars (plus the bison vars far below) 111 | are macros in the reentrant scanner. */ 112 | #define yyin yyg->yyin_r 113 | #define yyout yyg->yyout_r 114 | #define yyextra yyg->yyextra_r 115 | #define yyleng yyg->yyleng_r 116 | #define yytext yyg->yytext_r 117 | #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) 118 | #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) 119 | #define yy_flex_debug yyg->yy_flex_debug_r 120 | 121 | /* Size of default input buffer. */ 122 | #ifndef YY_BUF_SIZE 123 | #ifdef __ia64__ 124 | /* On IA-64, the buffer size is 16k, not 8k. 125 | * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. 126 | * Ditto for the __ia64__ case accordingly. 127 | */ 128 | #define YY_BUF_SIZE 32768 129 | #else 130 | #define YY_BUF_SIZE 16384 131 | #endif /* __ia64__ */ 132 | #endif 133 | 134 | #ifndef YY_TYPEDEF_YY_BUFFER_STATE 135 | #define YY_TYPEDEF_YY_BUFFER_STATE 136 | typedef struct yy_buffer_state *YY_BUFFER_STATE; 137 | #endif 138 | 139 | #ifndef YY_TYPEDEF_YY_SIZE_T 140 | #define YY_TYPEDEF_YY_SIZE_T 141 | typedef size_t yy_size_t; 142 | #endif 143 | 144 | #ifndef YY_STRUCT_YY_BUFFER_STATE 145 | #define YY_STRUCT_YY_BUFFER_STATE 146 | struct yy_buffer_state 147 | { 148 | FILE *yy_input_file; 149 | 150 | char *yy_ch_buf; /* input buffer */ 151 | char *yy_buf_pos; /* current position in input buffer */ 152 | 153 | /* Size of input buffer in bytes, not including room for EOB 154 | * characters. 155 | */ 156 | int yy_buf_size; 157 | 158 | /* Number of characters read into yy_ch_buf, not including EOB 159 | * characters. 160 | */ 161 | int yy_n_chars; 162 | 163 | /* Whether we "own" the buffer - i.e., we know we created it, 164 | * and can realloc() it to grow it, and should free() it to 165 | * delete it. 166 | */ 167 | int yy_is_our_buffer; 168 | 169 | /* Whether this is an "interactive" input source; if so, and 170 | * if we're using stdio for input, then we want to use getc() 171 | * instead of fread(), to make sure we stop fetching input after 172 | * each newline. 173 | */ 174 | int yy_is_interactive; 175 | 176 | /* Whether we're considered to be at the beginning of a line. 177 | * If so, '^' rules will be active on the next match, otherwise 178 | * not. 179 | */ 180 | int yy_at_bol; 181 | 182 | int yy_bs_lineno; /**< The line count. */ 183 | int yy_bs_column; /**< The column count. */ 184 | 185 | /* Whether to try to fill the input buffer when we reach the 186 | * end of it. 187 | */ 188 | int yy_fill_buffer; 189 | 190 | int yy_buffer_status; 191 | 192 | }; 193 | #endif /* !YY_STRUCT_YY_BUFFER_STATE */ 194 | 195 | void yyrestart (FILE *input_file ,yyscan_t yyscanner ); 196 | void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); 197 | YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); 198 | void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); 199 | void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); 200 | void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); 201 | void yypop_buffer_state (yyscan_t yyscanner ); 202 | 203 | YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); 204 | YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); 205 | YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); 206 | 207 | void *yyalloc (yy_size_t ,yyscan_t yyscanner ); 208 | void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); 209 | void yyfree (void * ,yyscan_t yyscanner ); 210 | 211 | /* Begin user sect3 */ 212 | 213 | #define yywrap(yyscanner) (/*CONSTCOND*/1) 214 | #define YY_SKIP_YYWRAP 215 | 216 | #define yytext_ptr yytext_r 217 | 218 | #ifdef YY_HEADER_EXPORT_START_CONDITIONS 219 | #define INITIAL 0 220 | #define COMMENT 1 221 | 222 | #endif 223 | 224 | #ifndef YY_NO_UNISTD_H 225 | /* Special case for "unistd.h", since it is non-ANSI. We include it way 226 | * down here because we want the user's section 1 to have been scanned first. 227 | * The user has a chance to override it with an option. 228 | */ 229 | #include 230 | #endif 231 | 232 | #ifndef YY_EXTRA_TYPE 233 | #define YY_EXTRA_TYPE void * 234 | #endif 235 | 236 | int yylex_init (yyscan_t* scanner); 237 | 238 | int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); 239 | 240 | /* Accessor methods to globals. 241 | These are made visible to non-reentrant scanners for convenience. */ 242 | 243 | int yylex_destroy (yyscan_t yyscanner ); 244 | 245 | int yyget_debug (yyscan_t yyscanner ); 246 | 247 | void yyset_debug (int debug_flag ,yyscan_t yyscanner ); 248 | 249 | YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); 250 | 251 | void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); 252 | 253 | FILE *yyget_in (yyscan_t yyscanner ); 254 | 255 | void yyset_in (FILE * _in_str ,yyscan_t yyscanner ); 256 | 257 | FILE *yyget_out (yyscan_t yyscanner ); 258 | 259 | void yyset_out (FILE * _out_str ,yyscan_t yyscanner ); 260 | 261 | int yyget_leng (yyscan_t yyscanner ); 262 | 263 | char *yyget_text (yyscan_t yyscanner ); 264 | 265 | int yyget_lineno (yyscan_t yyscanner ); 266 | 267 | void yyset_lineno (int _line_number ,yyscan_t yyscanner ); 268 | 269 | int yyget_column (yyscan_t yyscanner ); 270 | 271 | void yyset_column (int _column_no ,yyscan_t yyscanner ); 272 | 273 | YYSTYPE * yyget_lval (yyscan_t yyscanner ); 274 | 275 | void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); 276 | 277 | YYLTYPE *yyget_lloc (yyscan_t yyscanner ); 278 | 279 | void yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner ); 280 | 281 | /* Macros after this point can all be overridden by user definitions in 282 | * section 1. 283 | */ 284 | 285 | #ifndef YY_SKIP_YYWRAP 286 | #ifdef __cplusplus 287 | extern "C" int yywrap (yyscan_t yyscanner ); 288 | #else 289 | extern int yywrap (yyscan_t yyscanner ); 290 | #endif 291 | #endif 292 | 293 | #ifndef yytext_ptr 294 | static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); 295 | #endif 296 | 297 | #ifdef YY_NEED_STRLEN 298 | static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); 299 | #endif 300 | 301 | #ifndef YY_NO_INPUT 302 | 303 | #endif 304 | 305 | /* Amount of stuff to slurp up with each read. */ 306 | #ifndef YY_READ_BUF_SIZE 307 | #ifdef __ia64__ 308 | /* On IA-64, the buffer size is 16k, not 8k */ 309 | #define YY_READ_BUF_SIZE 16384 310 | #else 311 | #define YY_READ_BUF_SIZE 8192 312 | #endif /* __ia64__ */ 313 | #endif 314 | 315 | /* Number of entries by which start-condition stack grows. */ 316 | #ifndef YY_START_STACK_INCR 317 | #define YY_START_STACK_INCR 25 318 | #endif 319 | 320 | /* Default declaration of generated scanner - a define so the user can 321 | * easily add parameters. 322 | */ 323 | #ifndef YY_DECL 324 | #define YY_DECL_IS_OURS 1 325 | 326 | extern int yylex \ 327 | (YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner); 328 | 329 | #define YY_DECL int yylex \ 330 | (YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) 331 | #endif /* !YY_DECL */ 332 | 333 | /* yy_get_previous_state - get the state just before the EOB char was reached */ 334 | 335 | #undef YY_NEW_FILE 336 | #undef YY_FLUSH_BUFFER 337 | #undef yy_set_bol 338 | #undef yy_new_buffer 339 | #undef yy_set_interactive 340 | #undef YY_DO_BEFORE_ACTION 341 | 342 | #ifdef YY_DECL_IS_OURS 343 | #undef YY_DECL_IS_OURS 344 | #undef YY_DECL 345 | #endif 346 | 347 | #line 135 "como_lexer.l" 348 | 349 | 350 | #line 351 "como_lexer.h" 351 | #undef yyIN_HEADER 352 | #endif /* yyHEADER_H */ 353 | -------------------------------------------------------------------------------- /vm/lang/como_lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef yyHEADER_H 2 | #define yyHEADER_H 1 3 | #define yyIN_HEADER 1 4 | 5 | #line 6 "como_lexer.h" 6 | 7 | #line 8 "como_lexer.h" 8 | 9 | #define YY_INT_ALIGNED short int 10 | 11 | /* A lexical scanner generated by flex */ 12 | 13 | #define FLEX_SCANNER 14 | #define YY_FLEX_MAJOR_VERSION 2 15 | #define YY_FLEX_MINOR_VERSION 6 16 | #define YY_FLEX_SUBMINOR_VERSION 0 17 | #if YY_FLEX_SUBMINOR_VERSION > 0 18 | #define FLEX_BETA 19 | #endif 20 | 21 | /* First, we deal with platform-specific or compiler-specific issues. */ 22 | 23 | /* begin standard C headers. */ 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* end standard C headers. */ 30 | 31 | /* flex integer type definitions */ 32 | 33 | #ifndef FLEXINT_H 34 | #define FLEXINT_H 35 | 36 | /* C99 systems have . Non-C99 systems may or may not. */ 37 | 38 | #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 39 | 40 | /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, 41 | * if you want the limit (max/min) macros for int types. 42 | */ 43 | #ifndef __STDC_LIMIT_MACROS 44 | #define __STDC_LIMIT_MACROS 1 45 | #endif 46 | 47 | #include 48 | typedef int8_t flex_int8_t; 49 | typedef uint8_t flex_uint8_t; 50 | typedef int16_t flex_int16_t; 51 | typedef uint16_t flex_uint16_t; 52 | typedef int32_t flex_int32_t; 53 | typedef uint32_t flex_uint32_t; 54 | #else 55 | typedef signed char flex_int8_t; 56 | typedef short int flex_int16_t; 57 | typedef int flex_int32_t; 58 | typedef unsigned char flex_uint8_t; 59 | typedef unsigned short int flex_uint16_t; 60 | typedef unsigned int flex_uint32_t; 61 | 62 | /* Limits of integral types. */ 63 | #ifndef INT8_MIN 64 | #define INT8_MIN (-128) 65 | #endif 66 | #ifndef INT16_MIN 67 | #define INT16_MIN (-32767-1) 68 | #endif 69 | #ifndef INT32_MIN 70 | #define INT32_MIN (-2147483647-1) 71 | #endif 72 | #ifndef INT8_MAX 73 | #define INT8_MAX (127) 74 | #endif 75 | #ifndef INT16_MAX 76 | #define INT16_MAX (32767) 77 | #endif 78 | #ifndef INT32_MAX 79 | #define INT32_MAX (2147483647) 80 | #endif 81 | #ifndef UINT8_MAX 82 | #define UINT8_MAX (255U) 83 | #endif 84 | #ifndef UINT16_MAX 85 | #define UINT16_MAX (65535U) 86 | #endif 87 | #ifndef UINT32_MAX 88 | #define UINT32_MAX (4294967295U) 89 | #endif 90 | 91 | #endif /* ! C99 */ 92 | 93 | #endif /* ! FLEXINT_H */ 94 | 95 | #ifdef __cplusplus 96 | 97 | /* The "const" storage-class-modifier is valid. */ 98 | #define YY_USE_CONST 99 | 100 | #else /* ! __cplusplus */ 101 | 102 | /* C99 requires __STDC__ to be defined as 1. */ 103 | #if defined (__STDC__) 104 | 105 | #define YY_USE_CONST 106 | 107 | #endif /* defined (__STDC__) */ 108 | #endif /* ! __cplusplus */ 109 | 110 | #ifdef YY_USE_CONST 111 | #define yyconst const 112 | #else 113 | #define yyconst 114 | #endif 115 | 116 | /* An opaque pointer. */ 117 | #ifndef YY_TYPEDEF_YY_SCANNER_T 118 | #define YY_TYPEDEF_YY_SCANNER_T 119 | typedef void* yyscan_t; 120 | #endif 121 | 122 | /* For convenience, these vars (plus the bison vars far below) 123 | are macros in the reentrant scanner. */ 124 | #define yyin yyg->yyin_r 125 | #define yyout yyg->yyout_r 126 | #define yyextra yyg->yyextra_r 127 | #define yyleng yyg->yyleng_r 128 | #define yytext yyg->yytext_r 129 | #define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) 130 | #define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) 131 | #define yy_flex_debug yyg->yy_flex_debug_r 132 | 133 | /* Size of default input buffer. */ 134 | #ifndef YY_BUF_SIZE 135 | #ifdef __ia64__ 136 | /* On IA-64, the buffer size is 16k, not 8k. 137 | * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. 138 | * Ditto for the __ia64__ case accordingly. 139 | */ 140 | #define YY_BUF_SIZE 32768 141 | #else 142 | #define YY_BUF_SIZE 16384 143 | #endif /* __ia64__ */ 144 | #endif 145 | 146 | #ifndef YY_TYPEDEF_YY_BUFFER_STATE 147 | #define YY_TYPEDEF_YY_BUFFER_STATE 148 | typedef struct yy_buffer_state *YY_BUFFER_STATE; 149 | #endif 150 | 151 | #ifndef YY_TYPEDEF_YY_SIZE_T 152 | #define YY_TYPEDEF_YY_SIZE_T 153 | typedef size_t yy_size_t; 154 | #endif 155 | 156 | #ifndef YY_STRUCT_YY_BUFFER_STATE 157 | #define YY_STRUCT_YY_BUFFER_STATE 158 | struct yy_buffer_state 159 | { 160 | FILE *yy_input_file; 161 | 162 | char *yy_ch_buf; /* input buffer */ 163 | char *yy_buf_pos; /* current position in input buffer */ 164 | 165 | /* Size of input buffer in bytes, not including room for EOB 166 | * characters. 167 | */ 168 | yy_size_t yy_buf_size; 169 | 170 | /* Number of characters read into yy_ch_buf, not including EOB 171 | * characters. 172 | */ 173 | int yy_n_chars; 174 | 175 | /* Whether we "own" the buffer - i.e., we know we created it, 176 | * and can realloc() it to grow it, and should free() it to 177 | * delete it. 178 | */ 179 | int yy_is_our_buffer; 180 | 181 | /* Whether this is an "interactive" input source; if so, and 182 | * if we're using stdio for input, then we want to use getc() 183 | * instead of fread(), to make sure we stop fetching input after 184 | * each newline. 185 | */ 186 | int yy_is_interactive; 187 | 188 | /* Whether we're considered to be at the beginning of a line. 189 | * If so, '^' rules will be active on the next match, otherwise 190 | * not. 191 | */ 192 | int yy_at_bol; 193 | 194 | int yy_bs_lineno; /**< The line count. */ 195 | int yy_bs_column; /**< The column count. */ 196 | 197 | /* Whether to try to fill the input buffer when we reach the 198 | * end of it. 199 | */ 200 | int yy_fill_buffer; 201 | 202 | int yy_buffer_status; 203 | 204 | }; 205 | #endif /* !YY_STRUCT_YY_BUFFER_STATE */ 206 | 207 | void yyrestart (FILE *input_file ,yyscan_t yyscanner ); 208 | void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); 209 | YY_BUFFER_STATE yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); 210 | void yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); 211 | void yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); 212 | void yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); 213 | void yypop_buffer_state (yyscan_t yyscanner ); 214 | 215 | YY_BUFFER_STATE yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); 216 | YY_BUFFER_STATE yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); 217 | YY_BUFFER_STATE yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner ); 218 | 219 | void *yyalloc (yy_size_t ,yyscan_t yyscanner ); 220 | void *yyrealloc (void *,yy_size_t ,yyscan_t yyscanner ); 221 | void yyfree (void * ,yyscan_t yyscanner ); 222 | 223 | /* Begin user sect3 */ 224 | 225 | #define yywrap(yyscanner) (/*CONSTCOND*/1) 226 | #define YY_SKIP_YYWRAP 227 | 228 | #define yytext_ptr yytext_r 229 | 230 | #ifdef YY_HEADER_EXPORT_START_CONDITIONS 231 | #define INITIAL 0 232 | 233 | #endif 234 | 235 | #ifndef YY_NO_UNISTD_H 236 | /* Special case for "unistd.h", since it is non-ANSI. We include it way 237 | * down here because we want the user's section 1 to have been scanned first. 238 | * The user has a chance to override it with an option. 239 | */ 240 | #include 241 | #endif 242 | 243 | #ifndef YY_EXTRA_TYPE 244 | #define YY_EXTRA_TYPE void * 245 | #endif 246 | 247 | int yylex_init (yyscan_t* scanner); 248 | 249 | int yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); 250 | 251 | /* Accessor methods to globals. 252 | These are made visible to non-reentrant scanners for convenience. */ 253 | 254 | int yylex_destroy (yyscan_t yyscanner ); 255 | 256 | int yyget_debug (yyscan_t yyscanner ); 257 | 258 | void yyset_debug (int debug_flag ,yyscan_t yyscanner ); 259 | 260 | YY_EXTRA_TYPE yyget_extra (yyscan_t yyscanner ); 261 | 262 | void yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); 263 | 264 | FILE *yyget_in (yyscan_t yyscanner ); 265 | 266 | void yyset_in (FILE * _in_str ,yyscan_t yyscanner ); 267 | 268 | FILE *yyget_out (yyscan_t yyscanner ); 269 | 270 | void yyset_out (FILE * _out_str ,yyscan_t yyscanner ); 271 | 272 | yy_size_t yyget_leng (yyscan_t yyscanner ); 273 | 274 | char *yyget_text (yyscan_t yyscanner ); 275 | 276 | int yyget_lineno (yyscan_t yyscanner ); 277 | 278 | void yyset_lineno (int _line_number ,yyscan_t yyscanner ); 279 | 280 | int yyget_column (yyscan_t yyscanner ); 281 | 282 | void yyset_column (int _column_no ,yyscan_t yyscanner ); 283 | 284 | YYSTYPE * yyget_lval (yyscan_t yyscanner ); 285 | 286 | void yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner ); 287 | 288 | /* Macros after this point can all be overridden by user definitions in 289 | * section 1. 290 | */ 291 | 292 | #ifndef YY_SKIP_YYWRAP 293 | #ifdef __cplusplus 294 | extern "C" int yywrap (yyscan_t yyscanner ); 295 | #else 296 | extern int yywrap (yyscan_t yyscanner ); 297 | #endif 298 | #endif 299 | 300 | #ifndef yytext_ptr 301 | static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); 302 | #endif 303 | 304 | #ifdef YY_NEED_STRLEN 305 | static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); 306 | #endif 307 | 308 | #ifndef YY_NO_INPUT 309 | 310 | #endif 311 | 312 | /* Amount of stuff to slurp up with each read. */ 313 | #ifndef YY_READ_BUF_SIZE 314 | #ifdef __ia64__ 315 | /* On IA-64, the buffer size is 16k, not 8k */ 316 | #define YY_READ_BUF_SIZE 16384 317 | #else 318 | #define YY_READ_BUF_SIZE 8192 319 | #endif /* __ia64__ */ 320 | #endif 321 | 322 | /* Number of entries by which start-condition stack grows. */ 323 | #ifndef YY_START_STACK_INCR 324 | #define YY_START_STACK_INCR 25 325 | #endif 326 | 327 | /* Default declaration of generated scanner - a define so the user can 328 | * easily add parameters. 329 | */ 330 | #ifndef YY_DECL 331 | #define YY_DECL_IS_OURS 1 332 | 333 | extern int yylex \ 334 | (YYSTYPE * yylval_param ,yyscan_t yyscanner); 335 | 336 | #define YY_DECL int yylex \ 337 | (YYSTYPE * yylval_param , yyscan_t yyscanner) 338 | #endif /* !YY_DECL */ 339 | 340 | /* yy_get_previous_state - get the state just before the EOB char was reached */ 341 | 342 | #undef YY_NEW_FILE 343 | #undef YY_FLUSH_BUFFER 344 | #undef yy_set_bol 345 | #undef yy_new_buffer 346 | #undef yy_set_interactive 347 | #undef YY_DO_BEFORE_ACTION 348 | 349 | #ifdef YY_DECL_IS_OURS 350 | #undef YY_DECL_IS_OURS 351 | #undef YY_DECL 352 | #endif 353 | 354 | #line 80 "lexer.l" 355 | 356 | 357 | #line 358 "como_lexer.h" 358 | #undef yyIN_HEADER 359 | #endif /* yyHEADER_H */ 360 | -------------------------------------------------------------------------------- /vm/vm2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #define COMO_WARNING 1 7 | #include "como_debug.h" 8 | 9 | #include "../como_opcode.h" 10 | 11 | /* Physical location tracking for debugging*/ 12 | typedef struct _como_mem_tracker { 13 | const char *file; 14 | const char *fn; 15 | int line; 16 | void *addr; 17 | struct _como_mem_tracker *next; 18 | int flags; 19 | } como_mem_tracker; 20 | 21 | static int gc_on = 0; 22 | 23 | typedef struct _como_frame { 24 | como_object *name; 25 | como_object *code; 26 | como_object *constants; 27 | como_object *locals; 28 | como_object **stack; 29 | /* TODO, more roots, one per each frame */ 30 | como_object *root; 31 | como_size_t pc; 32 | como_size_t sz; 33 | como_size_t sp; 34 | como_size_t nobjs; /* total objects currently */ 35 | como_size_t nobjslt; /* lifetime allocation count */ 36 | como_size_t mxobjs; 37 | /* block whose starting address is 8-byte aligned or 16-byte aligned */ 38 | como_mem_tracker *mtrack; 39 | } como_frame; 40 | 41 | static void do_gc(como_frame *frame); 42 | 43 | 44 | /* TODO, since functions can be stored as names, 45 | I should create a function_object, (a subclass of como_object 46 | which is basically just a frame object */ 47 | /* I can perhaps create a frame object with a code object as argument */ 48 | /* Then I can implement a constructor, destructor for the function object 49 | to be called when it is called, and finished from calling */ 50 | static void como_frame_dtor(como_frame *f) 51 | { 52 | como_object_dtor(f->name); 53 | como_object_dtor(f->code); 54 | /* Each time a function is called, these need to be cleared */ 55 | como_object_dtor(f->constants); 56 | 57 | como_map *map = (como_map *)f->locals; 58 | como_size_t i; 59 | for(i = 0; i < map->size; i++) 60 | { 61 | como_map_bucket *bucket = map->buckets[i]; 62 | while(bucket) { 63 | como_object_dtor(bucket->key); 64 | bucket = bucket->next; 65 | } 66 | } 67 | 68 | como_object_dtor(f->locals); 69 | 70 | como_mem_tracker *mtrack = f->mtrack; 71 | while(mtrack) { 72 | como_mem_tracker *next = mtrack->next; 73 | free(mtrack); 74 | mtrack = next; 75 | } 76 | 77 | free(f->stack); 78 | free(f); 79 | } 80 | 81 | static como_frame *como_frame_new(char *name) 82 | { 83 | como_frame *f = malloc(sizeof(*f)); 84 | 85 | f->name = como_stringfromstring(name); 86 | f->code = como_code_new(8); 87 | f->constants = como_array_new(8); 88 | f->locals = como_map_new(8); 89 | f->stack = malloc(sizeof(como_object *) * 255); 90 | f->mtrack = NULL; 91 | f->pc = 0; 92 | f->sp = 0; 93 | f->sz = 255; 94 | f->root = NULL; 95 | f->nobjs = 0; 96 | f->nobjslt = 0; 97 | f->mxobjs = 8; 98 | 99 | int i; 100 | for(i = 0; i < 255; i++) 101 | f->stack[i] = NULL; 102 | 103 | return f; 104 | } 105 | 106 | static void should_grow(como_frame *frame) 107 | { 108 | if(frame->sp >= frame->sz) 109 | { 110 | frame->sz = frame->sz * 2; 111 | frame->stack = realloc(frame->stack, sizeof(como_object *) * frame->sz); 112 | } 113 | } 114 | 115 | #define gc_new(frame, obj) \ 116 | gc_new_ex(frame, obj, __FUNCTION__, __FILE__, __LINE__) 117 | 118 | #define gc_new_noref(frame, obj) \ 119 | gc_new_noref_ex(frame, obj, __FUNCTION__, __FILE__, __LINE__) 120 | 121 | 122 | static como_object *gc_new_noref_ex( 123 | como_frame *frame, como_object *obj, const char *fn, const char *file, int lineno) 124 | { 125 | frame->nobjslt++; 126 | 127 | #if 0 128 | como_debug("tracking object %p, lifetime total is %ld, current cycle is %ld, max objects is %ld", 129 | (void *)obj, frame->nobjslt, frame->nobjs, frame->mxobjs); 130 | 131 | /* Internal checking for sanity checks*/ 132 | como_mem_tracker *tracker = frame->mtrack; 133 | while(tracker) 134 | { 135 | if(tracker->addr == (void *)obj) 136 | { 137 | como_error_noreturn( 138 | "double tracking detected, address %p, first allocated on line %d in %s, " 139 | "attempted allocation on line %d in %s", 140 | (void *)obj, tracker->line, tracker->fn, lineno, fn); 141 | } 142 | tracker = tracker->next; 143 | } 144 | #endif 145 | 146 | 147 | como_mem_tracker *mtracked = malloc(sizeof(como_mem_tracker)); 148 | mtracked->line = lineno; 149 | mtracked->file = file; 150 | mtracked->addr = (void *)obj; 151 | mtracked->flags = 0; 152 | mtracked->fn = fn; 153 | mtracked->next = frame->mtrack; 154 | frame->mtrack = mtracked; 155 | 156 | como_object *str = obj->type->obj_str(obj); 157 | 158 | como_debug("\t%p is of type %s with value `%s`", 159 | (void *)obj, como_type_name(obj), ((como_string *)str)->value); 160 | como_debug("\t\tallocated on line %d", lineno); 161 | 162 | como_object_dtor(str); 163 | 164 | if(frame->nobjs >= frame->mxobjs) 165 | { 166 | como_warning("running gc on current frame (%d)", gc_on); 167 | if(gc_on) { 168 | do_gc(frame); 169 | } 170 | } 171 | 172 | obj->next = frame->root; 173 | obj->flags = 0; 174 | 175 | frame->root = obj; 176 | 177 | frame->nobjs++; 178 | 179 | return obj; 180 | } 181 | 182 | static como_object *gc_new_ex( 183 | como_frame *frame, como_object *obj, const char *fn, const char *file, int lineno) 184 | { 185 | frame->nobjslt++; 186 | 187 | 188 | como_mem_tracker *mtracked = malloc(sizeof(como_mem_tracker)); 189 | mtracked->line = lineno; 190 | mtracked->file = file; 191 | mtracked->addr = (void *)obj; 192 | mtracked->flags = 0; 193 | mtracked->fn = fn; 194 | mtracked->next = frame->mtrack; 195 | frame->mtrack = mtracked; 196 | 197 | como_object *str = obj->type->obj_str(obj); 198 | 199 | como_warning("\t%p is of type %s with value `%s`", 200 | (void *)obj, como_type_name(obj), ((como_string *)str)->value); 201 | como_warning("\t\tallocated on line %d", lineno); 202 | 203 | como_object_dtor(str); 204 | 205 | if(frame->nobjs >= frame->mxobjs) 206 | { 207 | como_warning("running gc on current frame (%d)", gc_on); 208 | if(gc_on) { 209 | do_gc(frame); 210 | } 211 | } 212 | 213 | obj->next = frame->root; 214 | obj->flags = 1; 215 | 216 | frame->root = obj; 217 | 218 | frame->nobjs++; 219 | 220 | return obj; 221 | } 222 | 223 | static como_object *add(como_frame *f, como_object *a, como_object *b) 224 | { 225 | if(a->type->obj_binops != NULL && a->type->obj_binops->obj_add != NULL) { 226 | como_object *result = a->type->obj_binops->obj_add(a, b); 227 | 228 | como_debug("result of " COMO_PTR_FMT "->type->obj_binops->obj_add(" COMO_PTR_FMT 229 | "," COMO_PTR_FMT ")", 230 | COMO_PTR(a), COMO_PTR(a), COMO_PTR(b)); 231 | 232 | como_debug("\tis %p", (void *)result); 233 | 234 | assert((void *)result != (void *)b); 235 | 236 | return gc_new(f, result); 237 | } 238 | return NULL; 239 | } 240 | 241 | static como_object *sub(como_frame *f, como_object *a, como_object *b) 242 | { 243 | if(a->type->obj_binops != NULL && a->type->obj_binops->obj_sub != NULL) 244 | return gc_new(f, a->type->obj_binops->obj_sub(a, b)); 245 | return NULL; 246 | } 247 | 248 | static como_object *mul(como_frame *f, como_object *a, como_object *b) 249 | { 250 | if(a->type->obj_binops != NULL && a->type->obj_binops->obj_mul != NULL) 251 | return gc_new(f, a->type->obj_binops->obj_mul(a, b)); 252 | return NULL; 253 | } 254 | 255 | static como_object *rem(como_frame *f, como_object *a, como_object *b) 256 | { 257 | if(a->type->obj_binops != NULL && a->type->obj_binops->obj_rem != NULL) 258 | return gc_new(f, a->type->obj_binops->obj_rem(a, b)); 259 | return NULL; 260 | } 261 | 262 | static como_object *do_div(como_frame *f, como_object *a, como_object *b) 263 | { 264 | if(a->type->obj_binops != NULL && a->type->obj_binops->obj_div != NULL) 265 | return gc_new(f, a->type->obj_binops->obj_div(a, b)); 266 | return NULL; 267 | } 268 | 269 | static como_object *unaryminus(como_frame *f, como_object *a) 270 | { 271 | if(a->type->obj_binops != NULL && a->type->obj_unops->obj_minus != NULL) 272 | return gc_new(f, a->type->obj_unops->obj_minus(a)); 273 | return NULL; 274 | } 275 | 276 | static como_object *unaryplus(como_frame *f, como_object *a) 277 | { 278 | if(a->type->obj_binops != NULL && a->type->obj_unops->obj_plus != NULL) 279 | return gc_new(f, a->type->obj_unops->obj_plus(a)); 280 | return NULL; 281 | } 282 | 283 | static como_object *isequal(como_frame *f, como_object *a, como_object *b) 284 | { 285 | return gc_new(f, a->type->obj_compops->obj_eq(a, b)); 286 | } 287 | 288 | static como_object *nequal(como_frame *f, como_object *a, como_object *b) 289 | { 290 | return gc_new(f, a->type->obj_compops->obj_neq(a, b)); 291 | } 292 | 293 | static como_object *isgt(como_frame *f, como_object *a, como_object *b) 294 | { 295 | return gc_new(f, a->type->obj_compops->obj_gt(a, b)); 296 | } 297 | 298 | static como_object *islt(como_frame *f, como_object *a, como_object *b) 299 | { 300 | return gc_new(f, a->type->obj_compops->obj_lt(a, b)); 301 | } 302 | 303 | static como_object *islte(como_frame *f, como_object *a, como_object *b) 304 | { 305 | return gc_new(f, a->type->obj_compops->obj_lte(a, b)); 306 | } 307 | 308 | static como_object *isgte(como_frame *f, como_object *a, como_object *b) 309 | { 310 | return gc_new(f, a->type->obj_compops->obj_gte(a, b)); 311 | } 312 | 313 | /* C truthness */ 314 | static int truthy(como_object *a) 315 | { 316 | return a->type->obj_bool(a) != 0; 317 | } 318 | 319 | static char *make_except(const char *fmt, ...) 320 | { 321 | char *buffer = NULL; 322 | int size = 1024; 323 | va_list args; 324 | 325 | va_start (args, fmt); 326 | buffer = malloc(size + 1); 327 | vsnprintf(buffer, size, fmt, args); 328 | va_end (args); 329 | return buffer; 330 | } 331 | 332 | /* 333 | * TODO, currently max index is USHRT_MAX, but there are no checks for that 334 | * limit... 335 | */ 336 | #define int_const(frame, value) \ 337 | (como_array_push(frame->constants, gc_new(frame, como_longfromlong(value))), \ 338 | (como_container_size(frame->constants) - 1)) 339 | 340 | #define str_const(frame, value) \ 341 | (como_array_push(frame->constants, gc_new(frame, como_stringfromstring(value))), \ 342 | (como_container_size(frame->constants) - 1)) 343 | 344 | #define str_const_noref(frame, value) \ 345 | (como_array_push(frame->constants, gc_new_noref(frame, como_stringfromstring(value))), \ 346 | (como_container_size(frame->constants) - 1)) 347 | 348 | 349 | #define dbl_const(frame, value) \ 350 | (como_array_push(frame->constants, gc_new(frame, como_doublefromdouble(value))), \ 351 | (como_container_size(frame->constants) - 1)) 352 | 353 | #define PACK_INSTRUCTION(opcode, argument, flag) \ 354 | (como_uint32_t)((((uint8_t)(opcode)) << 24) | \ 355 | (((uint16_t)(argument)) << 8) | \ 356 | ((uint8_t)(flag))) 357 | 358 | #define emit(frame, opcode, arg, flag) \ 359 | como_code_push(frame->code, PACK_INSTRUCTION(opcode, arg, flag)) 360 | 361 | static como_object *como_frame_eval(como_frame *frame) 362 | { 363 | #define vm_case(o) switch(o) 364 | 365 | #define vm_target(x) case x: 366 | 367 | #define vm_continue() break 368 | 369 | #define fetch() (frame->pc++, (((como_code_get(frame->code, frame->pc -1) >> 24) & 0xff))) 370 | 371 | #define get_const(x) \ 372 | como_array_get(frame->constants, x) 373 | 374 | #define get_arg() \ 375 | ((como_code_get(frame->code, frame->pc -1) >> 8) & 0xffff) 376 | 377 | #define push(arg) \ 378 | (should_grow(frame), (frame->stack[frame->sp++] = arg)) 379 | 380 | #define pop() \ 381 | (frame->stack[--frame->sp]) 382 | 383 | #define empty() \ 384 | (frame->sp == 0) 385 | 386 | #define getflag() (((como_code_get(frame->code, frame->pc -1)) & 0xff)) 387 | 388 | #define decref(o) do { \ 389 | --o->flags; \ 390 | } while(0); 391 | 392 | #define incref(o) o->flags++ 393 | 394 | como_object *arg; 395 | como_object *retval = NULL; 396 | como_object *left, *right, *result; 397 | char *ex = NULL; 398 | 399 | #define set_except(fmt, ...) \ 400 | ex = make_except(fmt, ##__VA_ARGS__) 401 | 402 | for(;;) { 403 | top: 404 | /* Reset result */ 405 | result = NULL; 406 | vm_case(fetch()) { 407 | vm_target(JMP) { 408 | frame->pc = get_arg(); 409 | goto top; 410 | } 411 | vm_target(JZ) { 412 | result = pop(); 413 | if(!truthy(result)) { 414 | frame->pc = (como_size_t)get_arg(); 415 | goto top; 416 | } 417 | vm_continue(); 418 | } 419 | vm_target(LOAD_CONST) { 420 | arg = get_const(get_arg()); 421 | push(arg); 422 | vm_continue(); 423 | } 424 | vm_target(STORE_NAME) { 425 | arg = get_const(get_arg()); 426 | result = pop(); 427 | como_map_put(frame->locals, arg, result); 428 | decref(arg); 429 | incref(result); 430 | push(result); 431 | vm_continue(); 432 | } 433 | vm_target(LOAD_NAME){ 434 | arg = get_const(get_arg()); 435 | result = como_map_get(frame->locals, arg); 436 | if(result) { 437 | push(result); 438 | } 439 | else { 440 | set_except("NameError, undefined variable `%s`", ((como_string *)arg)->value); 441 | } 442 | decref(arg); 443 | vm_continue(); 444 | } 445 | vm_target(IADD) { 446 | right = pop(); 447 | left = pop(); 448 | result = add(frame, left, right); 449 | 450 | decref(right); 451 | decref(left); 452 | 453 | if(result) { 454 | push(result); 455 | } 456 | else 457 | set_except("unsupported operands for + operator"); 458 | vm_continue(); 459 | } 460 | vm_target(IMINUS) { 461 | right = pop(); 462 | left = pop(); 463 | result = sub(frame, left, right); 464 | 465 | decref(right); 466 | decref(left); 467 | 468 | if(result) { 469 | push(result); 470 | } 471 | else 472 | set_except("unsupported operands for - operator"); 473 | vm_continue(); 474 | } 475 | vm_target(ITIMES) { 476 | right = pop(); 477 | left = pop(); 478 | result = mul(frame, left, right); 479 | 480 | decref(right); 481 | decref(left); 482 | 483 | if(result) { 484 | push(result); 485 | } 486 | else 487 | set_except("unsupported operands for * operator"); 488 | vm_continue(); 489 | } 490 | vm_target(IDIV) { 491 | right = pop(); 492 | left = pop(); 493 | result = do_div(frame, left, right); 494 | 495 | decref(right); 496 | decref(left); 497 | 498 | if(result) { 499 | push(result); 500 | } 501 | else 502 | set_except("unsupported operands for / operator"); 503 | vm_continue(); 504 | } 505 | vm_target(IREM) { 506 | right = pop(); 507 | left = pop(); 508 | result = rem(frame, left, right); 509 | 510 | decref(right); 511 | decref(left); 512 | 513 | if(result) { 514 | push(result); 515 | } 516 | else 517 | set_except("unsupported operands for % operator"); 518 | vm_continue(); 519 | } 520 | vm_target(UNARY_MINUS) { 521 | left = pop(); 522 | result = unaryminus(frame, left); 523 | if(result) 524 | push(result); 525 | else 526 | set_except("unsupported operand for unary -"); 527 | vm_continue(); 528 | } 529 | vm_target(UNARY_PLUS) { 530 | left = pop(); 531 | result = unaryplus(frame, left); 532 | if(result) 533 | push(result); 534 | else 535 | set_except("unsupported operand for unary +"); 536 | vm_continue(); 537 | } 538 | vm_target(IRETURN) { 539 | /* getflag() determine if the frame is returning a value */ 540 | if(getflag() && !empty()) { 541 | retval = pop(); 542 | } 543 | goto exit; 544 | } 545 | vm_target(IPRINT) { 546 | result = pop(); 547 | como_object_print(result); 548 | decref(result); 549 | fputc('\n', stdout); 550 | vm_continue(); 551 | } 552 | vm_target(EQUAL) { 553 | right = pop(); 554 | left = pop(); 555 | result = isequal(frame, left, right); 556 | 557 | decref(right); 558 | decref(left); 559 | 560 | push(result); 561 | vm_continue(); 562 | } 563 | vm_target(NEQUAL) { 564 | right = pop(); 565 | left = pop(); 566 | result = nequal(frame, left, right); 567 | push(result); 568 | vm_continue(); 569 | } 570 | vm_target(GT) { 571 | right = pop(); 572 | left = pop(); 573 | result = isgt(frame, left, right); 574 | push(result); 575 | vm_continue(); 576 | } 577 | vm_target(LT) { 578 | right = pop(); 579 | left = pop(); 580 | result = islt(frame, left, right); 581 | push(result); 582 | vm_continue(); 583 | } 584 | vm_target(GTE) { 585 | right = pop(); 586 | left = pop(); 587 | result = isgte(frame, left, right); 588 | push(result); 589 | vm_continue(); 590 | } 591 | vm_target(LTE) { 592 | right = pop(); 593 | left = pop(); 594 | result = islte(frame, left, right); 595 | push(result); 596 | vm_continue(); 597 | } 598 | } 599 | 600 | if(ex) { 601 | /* Find the exception handler */ 602 | como_size_t cpc = frame->pc; 603 | while(--cpc) 604 | { 605 | como_uint32_t opline; 606 | opline = como_code_get(frame->code, cpc); 607 | if(((opline >> 24) & 0xff) == TRY) 608 | { 609 | frame->pc = (opline >> 8) & 0xffff; 610 | 611 | /* Now, determine what the variable name is for the catch statement */ 612 | como_uint32_t nextop = como_code_get(frame->code, frame->pc + 1); 613 | if(((nextop >> 24) & 0xff) == LOAD_NAME) 614 | { 615 | como_size_t constindex = (nextop >> 8) & 0xffff; 616 | como_object *name = get_const(constindex); 617 | como_map_put(frame->locals, name, gc_new(frame, como_stringfromstring( 618 | (char *)ex))); 619 | } 620 | free(ex); 621 | ex = NULL; 622 | goto top; 623 | } 624 | } 625 | fprintf(stdout, "como: fatal, unhandled exception: %s\n", ex); 626 | free(ex); 627 | ex = NULL; 628 | goto exit; 629 | } 630 | } 631 | 632 | exit: 633 | while(!empty()) 634 | decref(pop()); 635 | 636 | do_gc(frame); 637 | como_frame_dtor(frame); 638 | 639 | return retval; 640 | } 641 | 642 | 643 | static void do_gc(como_frame *frame) 644 | { 645 | como_object **root = &frame->root; 646 | 647 | while(*root) 648 | { 649 | if((*root)->flags <= 0 ) { 650 | como_object *unreached = *root; 651 | como_warning("como_object_dtor(%p)", (void *)unreached); 652 | como_object *next = unreached->next; 653 | como_object_dtor(unreached); 654 | *root = next; 655 | frame->nobjs--; 656 | } 657 | else { 658 | root = &(*root)->next; 659 | } 660 | } 661 | } 662 | 663 | 664 | int main(void) 665 | { 666 | 667 | como_frame *mainframe = como_frame_new("main"); 668 | 669 | /* 670 | * sum = 15 + 15; 671 | * print(sum * 5 == 100); 672 | * print(2.455 % 2); 673 | * if(sum == 30) 674 | * { 675 | * print("sum is 30"); 676 | * } 677 | * print("returning from main"); 678 | * 679 | * try 680 | * { 681 | * name = "This" - "This"; 682 | * print("not reached..."); 683 | * } 684 | * catch(e) 685 | * { 686 | * print(e); 687 | * } 688 | * 689 | * 690 | * 691 | */ 692 | /* 693 | * BEGIN_TRY 694 | * 695 | * 696 | * 697 | * CATCH 698 | * 699 | * 700 | * END_TRY 701 | */ 702 | /* 703 | * print(sum = 15 + 5) 704 | */ 705 | 706 | /* 707 | at the end of each frame, destroy all variable names 708 | * TODO 709 | * separate root for all variable names 710 | */ 711 | emit(mainframe, LOAD_CONST, int_const(mainframe, 15), 0); 712 | emit(mainframe, LOAD_CONST, int_const(mainframe, 5), 0); 713 | emit(mainframe, IADD, 0, 0); 714 | emit(mainframe, STORE_NAME, str_const(mainframe, "sum"), 0); 715 | emit(mainframe, IPRINT, 0, 0); 716 | 717 | /* Testing the sumcopy = sum; */ 718 | emit(mainframe, LOAD_NAME, str_const(mainframe, "sum"), 0); 719 | emit(mainframe, STORE_NAME, str_const(mainframe, "sumcopy"), 0); 720 | 721 | emit(mainframe, LOAD_NAME, str_const(mainframe, "sum"), 0); 722 | emit(mainframe, LOAD_CONST, int_const(mainframe, 5), 0); 723 | emit(mainframe, ITIMES, 0, 0); 724 | emit(mainframe, LOAD_CONST, int_const(mainframe, 100), 0); 725 | emit(mainframe, EQUAL, 0, 0); 726 | emit(mainframe, IPRINT, 0, 0); 727 | emit(mainframe, LOAD_CONST, dbl_const(mainframe, 2.455), 0); 728 | emit(mainframe, LOAD_CONST, int_const(mainframe, 2), 0); 729 | emit(mainframe, IREM, 0, 0); 730 | emit(mainframe, IPRINT, 0, 0); 731 | emit(mainframe, LOAD_CONST, int_const(mainframe, 30), 0); 732 | emit(mainframe, LOAD_CONST, int_const(mainframe, 30), 0); 733 | emit(mainframe, EQUAL, 0, 0); 734 | emit(mainframe, TRY, como_container_size(mainframe->code) + 6, 0); 735 | emit(mainframe, LOAD_CONST, str_const(mainframe, "This "), 0); 736 | emit(mainframe, LOAD_CONST, str_const(mainframe, "This"), 0); 737 | emit(mainframe, IADD, 0, 0); 738 | emit(mainframe, IPRINT, 0, 0); 739 | emit(mainframe, JMP, como_container_size(mainframe->code) + 4, 0); 740 | emit(mainframe, CATCH, 0, 0); 741 | /* all conditional branches, need to have their refs set to 0 */ 742 | emit(mainframe, LOAD_NAME, str_const_noref(mainframe, "e"), 0); 743 | emit(mainframe, IPRINT, 0, 0); 744 | /* End try block */ 745 | emit(mainframe, LOAD_CONST, str_const(mainframe, "returning "), 0); 746 | emit(mainframe, LOAD_CONST, str_const(mainframe, "from main"), 0); 747 | emit(mainframe, IADD, 0, 0); 748 | emit(mainframe, IPRINT, 0, 0); 749 | emit(mainframe, IRETURN, 0, 1); 750 | 751 | /* We can't start running the GC until the program is executing */ 752 | gc_on = 1; 753 | 754 | como_object *retval = como_frame_eval(mainframe); 755 | 756 | assert(--retval->flags == 0); 757 | 758 | como_object_dtor(retval); 759 | 760 | return 0; 761 | } 762 | --------------------------------------------------------------------------------