mirror of
				https://github.com/egormanga/Slang.git
				synced 2025-11-04 02:54:59 +03:00 
			
		
		
		
	Huge work; i'm too tired to write commit message for all of it. Pleez let me sleep acouple of hours. Made most tests work. Started SBC.
This commit is contained in:
		
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,7 +1,8 @@
 | 
			
		||||
old.c
 | 
			
		||||
**.db
 | 
			
		||||
**.log
 | 
			
		||||
**.old
 | 
			
		||||
**.pyc
 | 
			
		||||
**.old
 | 
			
		||||
**.bak
 | 
			
		||||
**__pycache__
 | 
			
		||||
**_config.py
 | 
			
		||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
[submodule "compilers/pyssembly/typeshed"]
 | 
			
		||||
	path = compilers/pyssembly/typeshed
 | 
			
		||||
	url = https://github.com/python/typeshed.git
 | 
			
		||||
							
								
								
									
										50
									
								
								SBC/builtins.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								SBC/builtins.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
typedef struct atom {
 | 
			
		||||
	enum {b, i8, u8, i16, u16, i32, u32, i64, u64, i128, u128} type;
 | 
			
		||||
	union {
 | 
			
		||||
		_Bool* b;
 | 
			
		||||
		int8_t* i8;
 | 
			
		||||
		uint8_t* u8;
 | 
			
		||||
		int16_t* i16;
 | 
			
		||||
		uint16_t* u16;
 | 
			
		||||
		int32_t* i32;
 | 
			
		||||
		uint32_t* u32;
 | 
			
		||||
		int64_t* i64;
 | 
			
		||||
		uint64_t* u64;
 | 
			
		||||
		void* data;
 | 
			
		||||
	};
 | 
			
		||||
} atom_t;
 | 
			
		||||
 | 
			
		||||
typedef atom_t (*builtin_function)(int nargs, atom_t args[nargs]);
 | 
			
		||||
 | 
			
		||||
typedef struct builtin {
 | 
			
		||||
	const char* name;
 | 
			
		||||
	builtin_function fp;
 | 
			
		||||
} builtin_t;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// XXX ///
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
atom_t _builtin_println(int nargs, atom_t args[nargs]) {
 | 
			
		||||
	static int res;
 | 
			
		||||
	res = puts(args[0].data);
 | 
			
		||||
	return (atom_t){i32, .i32 = &res};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// XXX ///
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
builtin_t builtins[] = {
 | 
			
		||||
	{"println", _builtin_println},
 | 
			
		||||
{NULL, NULL}};
 | 
			
		||||
 | 
			
		||||
builtin_function get_builtin(const char* name) {
 | 
			
		||||
	for (builtin_t* i = builtins; i->name != NULL; i++)
 | 
			
		||||
		if (strcmp(i->name, name) == 0)
 | 
			
		||||
			return i->fp;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
@@ -6,9 +6,22 @@
 | 
			
		||||
### NOP
 | 
			
		||||
0x00
 | 
			
		||||
> Does nothing.
 | 
			
		||||
### RET
 | 
			
		||||
### END
 | 
			
		||||
0x01
 | 
			
		||||
> Closes previously opened block.
 | 
			
		||||
### POP
 | 
			
		||||
0x02
 | 
			
		||||
> Drops `TOS`.
 | 
			
		||||
### RET
 | 
			
		||||
0x03
 | 
			
		||||
> Returns `TOS` to caller.
 | 
			
		||||
### BLTIN
 | 
			
		||||
0x04
 | 
			
		||||
> Reads string from the bytecode until null byte (max 255 bytes) and pushes builtin function with that name.
 | 
			
		||||
### CODE
 | 
			
		||||
0x05
 | 
			
		||||
> Reads code from the bytecode until the corresponding `END` instruction and pushes a reference to it and then its length.
 | 
			
		||||
> Opens a block.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Unary
 | 
			
		||||
@@ -44,7 +57,7 @@
 | 
			
		||||
0x19
 | 
			
		||||
> Pushes integer of smallest possible width equal to rounded `TOS`.
 | 
			
		||||
### CTOS
 | 
			
		||||
0x1a
 | 
			
		||||
0x1A
 | 
			
		||||
> Pushes string consisting of char `TOS` and a null byte.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -62,7 +75,7 @@
 | 
			
		||||
### DIV
 | 
			
		||||
0x23
 | 
			
		||||
> Pushes `TOS1 / TOS`.
 | 
			
		||||
### FLRDIV
 | 
			
		||||
### IDIV
 | 
			
		||||
0x24
 | 
			
		||||
> Pushes `TOS1 // TOS`.
 | 
			
		||||
### MOD
 | 
			
		||||
@@ -71,46 +84,88 @@
 | 
			
		||||
### POW
 | 
			
		||||
0x26
 | 
			
		||||
> Pushes `TOS1 ** TOS`.
 | 
			
		||||
### SHL
 | 
			
		||||
### LSH
 | 
			
		||||
0x27
 | 
			
		||||
> Pushes `TOS1 << TOS`.
 | 
			
		||||
### SHR
 | 
			
		||||
### RSH
 | 
			
		||||
0x28
 | 
			
		||||
> Pushes `TOS1 >> TOS`.
 | 
			
		||||
### AND
 | 
			
		||||
0x29
 | 
			
		||||
> Pushes `TOS1 & TOS`.
 | 
			
		||||
### OR
 | 
			
		||||
0x2a
 | 
			
		||||
0x2A
 | 
			
		||||
> Pushes `TOS1 | TOS`.
 | 
			
		||||
### XOR
 | 
			
		||||
0x2b
 | 
			
		||||
0x2B
 | 
			
		||||
> Pushes `TOS1 ^ TOS`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Comparisons
 | 
			
		||||
 | 
			
		||||
### EQ
 | 
			
		||||
0x30
 | 
			
		||||
> Pushes `TOS1 == TOS`.
 | 
			
		||||
### NE
 | 
			
		||||
0x31
 | 
			
		||||
> Pushes `TOS1 != TOS`.
 | 
			
		||||
### LT
 | 
			
		||||
0x32
 | 
			
		||||
> Pushes `TOS1 < TOS`.
 | 
			
		||||
### GT
 | 
			
		||||
0x33
 | 
			
		||||
> Pushes `TOS1 > TOS`.
 | 
			
		||||
### LE
 | 
			
		||||
0x34
 | 
			
		||||
> Pushes `TOS1 <= TOS`.
 | 
			
		||||
### GE
 | 
			
		||||
0x35
 | 
			
		||||
> Pushes `TOS1 >= TOS`.
 | 
			
		||||
### IS
 | 
			
		||||
0x36
 | 
			
		||||
> Pushes `TOS1 is TOS`.
 | 
			
		||||
### ISNOT
 | 
			
		||||
0x37
 | 
			
		||||
> Pushes `TOS1 is not TOS`.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Flow control
 | 
			
		||||
### IF
 | 
			
		||||
0x40
 | 
			
		||||
> If `TOS` is false, skips bytecode until corresponding `ELSE` (if exists) or `END`.
 | 
			
		||||
> Opens a block.
 | 
			
		||||
### ELSE
 | 
			
		||||
0x41
 | 
			
		||||
> Pops last `IF` result from `IF`-stack, and if it is true, skips bytecode to corresponding `END`.
 | 
			
		||||
> Opens a block.
 | 
			
		||||
### EXEC
 | 
			
		||||
0x42
 | 
			
		||||
> Executes code block `TOS1` of length `TOS` and pushes the result.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## With argument
 | 
			
		||||
 | 
			
		||||
### ALLOC*(bytes)*
 | 
			
		||||
0xa0
 | 
			
		||||
0xA0
 | 
			
		||||
> Pushes reference to `calloc(1, bytes)`.
 | 
			
		||||
### EXTEND*(bytes)*
 | 
			
		||||
0xa1
 | 
			
		||||
0xA1
 | 
			
		||||
> Extends integer `TOS` width to `bytes` bytes if narrower.
 | 
			
		||||
### CONST*(bytes)*
 | 
			
		||||
0xa2
 | 
			
		||||
0xA2
 | 
			
		||||
> Reads next `bytes` bytes of bytecode and pushes a reference to a copy of them.
 | 
			
		||||
### JUMPF*(offset)*
 | 
			
		||||
0xa3
 | 
			
		||||
0xA3
 | 
			
		||||
> Jumps `offset` bytes of bytecode forward.
 | 
			
		||||
### JUMPB*(offset)*
 | 
			
		||||
0xa4
 | 
			
		||||
0xA4
 | 
			
		||||
> Jumps `offset` bytes of bytecode backward.
 | 
			
		||||
### SCPGET*(cell)*
 | 
			
		||||
0xa5
 | 
			
		||||
0xA5
 | 
			
		||||
> Pushes the value of cell `cell` of local scope variables array.
 | 
			
		||||
### SCPSET*(cell)*
 | 
			
		||||
0xa6
 | 
			
		||||
0xA6
 | 
			
		||||
> Sets the value of cell `cell` of local scope variables array to `TOS`.
 | 
			
		||||
### CALL*(nargs)*
 | 
			
		||||
0xa7
 | 
			
		||||
> Pops `nargs ^ (1 << 7)` TODO
 | 
			
		||||
0xA7
 | 
			
		||||
> Calls `TOS` with `nargs` arguments popped from stack (below the callable).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										50
									
								
								SBC/dissbc.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										50
									
								
								SBC/dissbc.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,50 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# SBC Disassembler
 | 
			
		||||
 | 
			
		||||
from utils.nolog import *
 | 
			
		||||
 | 
			
		||||
opcodes = {int(m[2], 16): m[1] for m in re.finditer(r'### (\w+).*?\n(\w+)', open(os.path.join(os.path.dirname(sys.argv[0]), 'bytecode.md')).read())}
 | 
			
		||||
HASARG = 0xa0
 | 
			
		||||
 | 
			
		||||
def dissbc(code, no_opnames=False):
 | 
			
		||||
	cp = 0
 | 
			
		||||
	codesize = len(code)
 | 
			
		||||
	blocklvl = 0
 | 
			
		||||
	while (cp < codesize):
 | 
			
		||||
		opcode = code[cp]; cp += 1
 | 
			
		||||
		opname = opcodes.get(opcode)
 | 
			
		||||
		if (opname == 'END'): blocklvl -= 1
 | 
			
		||||
		print('\t'*blocklvl, end='')
 | 
			
		||||
		if (opname is None): print("\033[2mUNKNOWN: %02x\033[0m" % opcode); continue
 | 
			
		||||
		print(f"\033[1m0x{opcode:02x}{' '+opname if (not no_opnames) else ''}\033[0m", end='')
 | 
			
		||||
		if (opcode > HASARG):
 | 
			
		||||
			arg = code[cp]; cp += 1
 | 
			
		||||
			print("(%d|0x%02x)" % (arg, arg), end='')
 | 
			
		||||
 | 
			
		||||
		if (opname == 'CONST'):
 | 
			
		||||
			const = code[cp:cp+arg]
 | 
			
		||||
			cp += arg
 | 
			
		||||
			print(':', '0x'+const.hex(), end='')
 | 
			
		||||
			print(' |', str().join(chr(i) if (32 <= i < 128) else '.' for i in const), end='')
 | 
			
		||||
		elif (opname == 'BLTIN'):
 | 
			
		||||
			name = bytearray()
 | 
			
		||||
			while (code[cp] != 0):
 | 
			
		||||
				name.append(code[cp]); cp += 1
 | 
			
		||||
			else: cp += 1
 | 
			
		||||
			print(':', name.decode('ascii'), end='')
 | 
			
		||||
 | 
			
		||||
		if (opname in ('IF', 'ELSE', 'CODE')):
 | 
			
		||||
			print(':', end='')
 | 
			
		||||
			blocklvl += 1
 | 
			
		||||
 | 
			
		||||
		print()
 | 
			
		||||
 | 
			
		||||
@apmain
 | 
			
		||||
@aparg('file', metavar='<file.sbc>')
 | 
			
		||||
@aparg('--no-opnames', action='store_true')
 | 
			
		||||
def main(cargs):
 | 
			
		||||
	dissbc(open(cargs.file, 'rb').read(), no_opnames=cargs.no_opnames)
 | 
			
		||||
 | 
			
		||||
if (__name__ == '__main__'): exit(main(nolog=True), nolog=True)
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
							
								
								
									
										217
									
								
								SBC/sbc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								SBC/sbc.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,217 @@
 | 
			
		||||
// SBC
 | 
			
		||||
 | 
			
		||||
#define _GNU_SOURCE
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include "builtins.c"
 | 
			
		||||
#include "stack.c"
 | 
			
		||||
 | 
			
		||||
typedef uint8_t code_t;
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	// Standalone
 | 
			
		||||
	NOP	= 0x00,
 | 
			
		||||
	END	= 0x01,
 | 
			
		||||
	POP	= 0x02,
 | 
			
		||||
	RET	= 0x03,
 | 
			
		||||
	BLTIN	= 0x04,
 | 
			
		||||
	CODE	= 0x05,
 | 
			
		||||
 | 
			
		||||
	// Unary
 | 
			
		||||
	POS	= 0x10,
 | 
			
		||||
	NEG	= 0x11,
 | 
			
		||||
	NOT	= 0x12,
 | 
			
		||||
	INV	= 0x13,
 | 
			
		||||
	ATOI	= 0x14,
 | 
			
		||||
	ITOA	= 0x15,
 | 
			
		||||
	ITOF	= 0x16,
 | 
			
		||||
	CEIL	= 0x17,
 | 
			
		||||
	FLR	= 0x18,
 | 
			
		||||
	RND	= 0x19,
 | 
			
		||||
	CTOS	= 0x1A,
 | 
			
		||||
 | 
			
		||||
	// Binary
 | 
			
		||||
	ADD	= 0x20,
 | 
			
		||||
	SUB	= 0x21,
 | 
			
		||||
	MUL	= 0x22,
 | 
			
		||||
	DIV	= 0x23,
 | 
			
		||||
	IDIV	= 0x24,
 | 
			
		||||
	MOD	= 0x25,
 | 
			
		||||
	POW	= 0x26,
 | 
			
		||||
	SHL	= 0x27,
 | 
			
		||||
	SHR	= 0x28,
 | 
			
		||||
	AND	= 0x29,
 | 
			
		||||
	OR	= 0x2A,
 | 
			
		||||
	XOR	= 0x2B,
 | 
			
		||||
 | 
			
		||||
	// Comparisons
 | 
			
		||||
	EQ	= 0x30,
 | 
			
		||||
	NE	= 0x31,
 | 
			
		||||
	LT	= 0x32,
 | 
			
		||||
	GT	= 0x33,
 | 
			
		||||
	LE	= 0x34,
 | 
			
		||||
	GE	= 0x35,
 | 
			
		||||
	IS	= 0x36,
 | 
			
		||||
	ISNOT	= 0x37,
 | 
			
		||||
 | 
			
		||||
	// Flow control
 | 
			
		||||
	IF	= 0x40,
 | 
			
		||||
	ELSE	= 0x41,
 | 
			
		||||
	EXEC	= 0x42,
 | 
			
		||||
 | 
			
		||||
	// With argument
 | 
			
		||||
	ALLOC	= 0xA0,
 | 
			
		||||
	EXTEND	= 0xA1,
 | 
			
		||||
	CONST	= 0xA2,
 | 
			
		||||
	JUMPF	= 0xA3,
 | 
			
		||||
	JUMPB	= 0xA4,
 | 
			
		||||
	SCPGET	= 0xA5,
 | 
			
		||||
	SCPSET	= 0xA6,
 | 
			
		||||
	CALL	= 0xA7,
 | 
			
		||||
 | 
			
		||||
	HASARG	= 0xA0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
atom_t exec(code_t* code, uint32_t codesize) { // TODO: freeing
 | 
			
		||||
	stack_t* st = stack();
 | 
			
		||||
	atom_t scp[255]; // TODO
 | 
			
		||||
	code_t* cb[255]; // TODO
 | 
			
		||||
	uint32_t cbi = 0;
 | 
			
		||||
 | 
			
		||||
	uint32_t cp = 0;
 | 
			
		||||
	while (cp < codesize) {
 | 
			
		||||
		code_t opcode = code[cp++];
 | 
			
		||||
		uint32_t ocp = cp;
 | 
			
		||||
 | 
			
		||||
		switch (opcode) {
 | 
			
		||||
			// Standalone
 | 
			
		||||
			case NOP: break;
 | 
			
		||||
			case END: break;
 | 
			
		||||
			case POP: stack_pop(st); break;
 | 
			
		||||
			case RET: return st->top->data;
 | 
			
		||||
			case BLTIN: {
 | 
			
		||||
				char name[256];
 | 
			
		||||
				strncpy(name, (char*)code+cp, 255);
 | 
			
		||||
				while (code[cp++] != '\0');
 | 
			
		||||
				stack_push(st, (atom_t){.data = get_builtin(name)});
 | 
			
		||||
			}; break;
 | 
			
		||||
			case CODE: {
 | 
			
		||||
				code_t* code_block = malloc(codesize);
 | 
			
		||||
				static uint32_t cbp = 0;
 | 
			
		||||
				uint32_t blocklvl = 1;
 | 
			
		||||
				while (cp < codesize) {
 | 
			
		||||
					code_t c = code[cp++];
 | 
			
		||||
					if (c == CODE	||
 | 
			
		||||
					    c == IF	||
 | 
			
		||||
					    c == ELSE) blocklvl++;
 | 
			
		||||
					else if (c == END) blocklvl--;
 | 
			
		||||
					if (blocklvl <= 0) break;
 | 
			
		||||
					code_block[cbp++] = c;
 | 
			
		||||
					if (c > HASARG) code_block[cbp++] = code[cp++];
 | 
			
		||||
					if (c == CONST)
 | 
			
		||||
						for (uint8_t i = code_block[cbp-1]; i > 0; i--)
 | 
			
		||||
							code_block[cbp++] = code[cp++];
 | 
			
		||||
					else if (c == BLTIN)
 | 
			
		||||
						do code_block[cbp++] = code[cp++];
 | 
			
		||||
						while (code[cp-1] != '\0');
 | 
			
		||||
				}
 | 
			
		||||
				cb[cbi++] = realloc(code_block, cbp);
 | 
			
		||||
				free(code_block);
 | 
			
		||||
				stack_push(st, (atom_t){u32, .u32 = &cbp});
 | 
			
		||||
				stack_push(st, (atom_t){.data = &cb[cbi-1]});
 | 
			
		||||
			}; break;
 | 
			
		||||
 | 
			
		||||
			// Unary
 | 
			
		||||
			case POS: *st->top->data.i32 = abs(*st->top->data.i32); break;
 | 
			
		||||
			case ITOA: {
 | 
			
		||||
				char s[12];
 | 
			
		||||
				fprintf(stderr, "-- %x\n", *st->top->data.i32);
 | 
			
		||||
				snprintf(s, sizeof(s)/sizeof(*s), "%d", *st->top->data.i32);
 | 
			
		||||
				st->top->data.data = strdup(s);
 | 
			
		||||
			}; break;
 | 
			
		||||
 | 
			
		||||
			// Binary (TODO)
 | 
			
		||||
			case ADD: *st->top->data.i32 += *stack_pop(st).i32; break;
 | 
			
		||||
			case SUB: *st->top->data.i32 -= *stack_pop(st).i32; break;
 | 
			
		||||
 | 
			
		||||
			// Comparisons
 | 
			
		||||
			case LT: *st->top->data.b = *stack_pop(st).i32 > *st->top->data.i32; break;
 | 
			
		||||
 | 
			
		||||
			// Flow control
 | 
			
		||||
			case EXEC: {
 | 
			
		||||
				uint32_t exec_codesize = *stack_pop(st).u32;
 | 
			
		||||
				code_t* exec_code = stack_pop(st).data;
 | 
			
		||||
				stack_push(st, exec(exec_code, exec_codesize));
 | 
			
		||||
			}; break;
 | 
			
		||||
 | 
			
		||||
			// With argument
 | 
			
		||||
			case CONST: {
 | 
			
		||||
				uint8_t len = code[cp++];
 | 
			
		||||
				stack_push(st, (atom_t){.data = memcpy(malloc(len), code+cp, len)});
 | 
			
		||||
				fprintf(stderr, "-- l=%02x: %x\n", len, *st->top->data.i32);
 | 
			
		||||
				cp += len;
 | 
			
		||||
			}; break;
 | 
			
		||||
			case SCPGET: {
 | 
			
		||||
				uint8_t cell = code[cp++];
 | 
			
		||||
				stack_push(st, scp[cell]);
 | 
			
		||||
			}; break;
 | 
			
		||||
			case SCPSET: {
 | 
			
		||||
				uint8_t cell = code[cp++];
 | 
			
		||||
				scp[cell] = stack_pop(st);
 | 
			
		||||
				fprintf(stderr, "-- c%d = %d\n", cell, *scp[cell].i32);
 | 
			
		||||
			}; break;
 | 
			
		||||
			case CALL: {
 | 
			
		||||
				uint8_t nargs = code[cp++];
 | 
			
		||||
				fprintf(stderr, "-- nargs=%d\n", nargs);
 | 
			
		||||
				builtin_function func = stack_pop(st).data;
 | 
			
		||||
				atom_t args[nargs];
 | 
			
		||||
				for (uint8_t i = 0; i < nargs; i++)
 | 
			
		||||
					args[i] = stack_pop(st);
 | 
			
		||||
				stack_push(st, func(nargs, args));
 | 
			
		||||
			}; break;
 | 
			
		||||
 | 
			
		||||
			default: fprintf(stderr, "Not Implemented opcode: 0x%02x\n", opcode); exit(3);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fprintf(stderr, "[%02x", opcode);
 | 
			
		||||
		if (opcode > HASARG) fprintf(stderr, "(%d|0x%02x)", code[ocp], code[ocp++]);
 | 
			
		||||
		fputc(':', stderr);
 | 
			
		||||
		do fprintf(stderr, " %02x", code[ocp++]);
 | 
			
		||||
		while (ocp < cp && ocp < codesize);
 | 
			
		||||
		if (st->top != NULL) {
 | 
			
		||||
			fprintf(stderr, ", TOS = %u(0x%02x)", *st->top->data.i32, *st->top->data.i32);
 | 
			
		||||
			if (isprint(*(char*)st->top->data.data)) {
 | 
			
		||||
				fprintf(stderr, " | ");
 | 
			
		||||
				for (char* p = st->top->data.data; *p != '\0'; p++)
 | 
			
		||||
					fputc(isprint(*p)?*p:'.', stderr);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fprintf(stderr, "]\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (atom_t){.data = NULL};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[]) {
 | 
			
		||||
	if (argc != 2) {
 | 
			
		||||
		fprintf(stderr, "Usage: %s <file.sbc>\n", basename(argv[0]));
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	FILE* fd = fopen(argv[1], "rb");
 | 
			
		||||
	fseek(fd, 0, SEEK_END);
 | 
			
		||||
	long fsize = ftell(fd);
 | 
			
		||||
	fseek(fd, 0, SEEK_SET);
 | 
			
		||||
 | 
			
		||||
	code_t code[fsize];
 | 
			
		||||
	fread(code, sizeof(*code), fsize, fd);
 | 
			
		||||
	fclose(fd);
 | 
			
		||||
 | 
			
		||||
	exec(code, fsize);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// by Sdore, 2020
 | 
			
		||||
							
								
								
									
										53
									
								
								SBC/stack.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								SBC/stack.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
/*
 | 
			
		||||
#define CAT(a, b) a##_##b
 | 
			
		||||
#define TEMPLATE(name, type) CAT(name, type)
 | 
			
		||||
 | 
			
		||||
#define stack		TEMPLATE(stack, T)
 | 
			
		||||
#define stack_t	TEMPLATE(stack_t, T)
 | 
			
		||||
#define stack_item	TEMPLATE(stack_item, T)
 | 
			
		||||
#define stack_push	TEMPLATE(stack_push, T)
 | 
			
		||||
#define stack_pop	TEMPLATE(stack_pop, T)
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#define T atom_t
 | 
			
		||||
 | 
			
		||||
typedef struct stack {
 | 
			
		||||
	struct stack_item* top;
 | 
			
		||||
} stack_t;
 | 
			
		||||
 | 
			
		||||
struct stack_item {
 | 
			
		||||
	T data;
 | 
			
		||||
	struct stack_item* below;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
stack_t* stack() {
 | 
			
		||||
	stack_t* st = malloc(sizeof(*st));
 | 
			
		||||
	st->top = NULL;
 | 
			
		||||
	return st;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void stack_push(stack_t* st, T data) {
 | 
			
		||||
	struct stack_item* new = malloc(sizeof(*new));
 | 
			
		||||
	new->data = data;
 | 
			
		||||
	if (st->top != NULL) new->below = st->top;
 | 
			
		||||
	st->top = new;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
T stack_pop(stack_t* st) {
 | 
			
		||||
	assert (st->top != NULL);
 | 
			
		||||
	struct stack_item* item = st->top;
 | 
			
		||||
	st->top = item->below;
 | 
			
		||||
	T data = item->data;
 | 
			
		||||
	free(item);
 | 
			
		||||
	return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
#undef stack
 | 
			
		||||
#undef stack_t
 | 
			
		||||
#undef stack_item
 | 
			
		||||
#undef stack_push
 | 
			
		||||
#undef stack_pop
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#undef T
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								SBC/tests/hw.sbc
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								SBC/tests/hw.sbc
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										23
									
								
								Slang.md
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Slang.md
									
									
									
									
									
								
							@@ -18,19 +18,17 @@ char f(str x) {  # f() is of type char, x is of type str
 | 
			
		||||
	return c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
char g(str x) {  # g() is of type char, x is of type str
 | 
			
		||||
auto g(str x) {  # g() is of type char, x is of type str
 | 
			
		||||
	return x[0]  # retval is of type char
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int h(int x) = x+1  # h() is of type int, x is of type int
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
	print(h(n), \
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(h(n), \  # comment here too
 | 
			
		||||
		f('123asd') + g('32') + 1)  #--> «123124 f»
 | 
			
		||||
	print(q/z/2**96)  #--> «4294967296.0»
 | 
			
		||||
	stdio.println(q/z/2**96)  #--> «4294967296.0»
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main()
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Tokens
 | 
			
		||||
@@ -83,6 +81,7 @@ _Note: `*` after syntax unit means any number of them._
 | 
			
		||||
* `<exprkeyword> [expr]` — `keywordexpr` (keyword expression)
 | 
			
		||||
* `<typedef> <identifier> [= <value>]` — `vardef` (variable definition)
 | 
			
		||||
* `<identifier> = <value>` — `assignment`
 | 
			
		||||
* `<identifier>[, <identifier>]* = <value>` — `unpackassignment`
 | 
			
		||||
* `<value>([<callargs> | <callkwargs> | <callargs>, <callkwargs>])` — `funccall` (function call)
 | 
			
		||||
* `<expr>` — expr evaluation (only in REPL)
 | 
			
		||||
* `if (<expr>) <block>` — `conditional`
 | 
			
		||||
@@ -106,7 +105,7 @@ _Note: `*` after syntax unit means any number of them._
 | 
			
		||||
 | 
			
		||||
Non-empty sequence of alphanumeric characters plus underscore («_»), not starting with a digit character.
 | 
			
		||||
 | 
			
		||||
Regex: `[_\w]+[_\w\d]*`
 | 
			
		||||
Regex: `[_\w][_\w\d]*`
 | 
			
		||||
 | 
			
		||||
### Data types
 | 
			
		||||
 | 
			
		||||
@@ -133,6 +132,12 @@ _Note: `*` after syntax unit here means any number of them, `+` means at least o
 | 
			
		||||
* `<<0<b | o | x>><digit+> | <digit+>.<digit*> | <digit*>.<digit+>>` — number
 | 
			
		||||
* `<"<character*>" | '<character*>'>` — string
 | 
			
		||||
 | 
			
		||||
### Literal structures
 | 
			
		||||
 | 
			
		||||
* `[ <value>[, <value>]* ]` — list
 | 
			
		||||
* `( [type] <value>[, [type] <value>]* )` — tuple
 | 
			
		||||
* `{ <<key>: <value>>* }` — map
 | 
			
		||||
 | 
			
		||||
## Operators
 | 
			
		||||
 | 
			
		||||
* `<operator> <operand>` — unary operators usage
 | 
			
		||||
@@ -143,7 +148,7 @@ _Note: `*` after syntax unit here means any number of them, `+` means at least o
 | 
			
		||||
 | 
			
		||||
A set of pre-defined character operators:
 | 
			
		||||
 | 
			
		||||
* `!$%&*+-:^~` — unary charset
 | 
			
		||||
* `!+-~` — unary charset
 | 
			
		||||
* `%&*+-/<=>^|` — binary charset
 | 
			
		||||
* `==`, `**`, `//`, `<<`, `>>` — double-char binary operators
 | 
			
		||||
 | 
			
		||||
@@ -167,4 +172,4 @@ A set of pre-defined keyword operators:
 | 
			
		||||
All character class checks are performed in current locale.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
_by Sdore, 2019_
 | 
			
		||||
_by Sdore, 2020_
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								Slang.py
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								Slang.py
									
									
									
									
									
								
							@@ -2,12 +2,10 @@
 | 
			
		||||
# Slang
 | 
			
		||||
 | 
			
		||||
from .ast import *
 | 
			
		||||
from .repl import *
 | 
			
		||||
from .compilers import *
 | 
			
		||||
from .compilers.pyssembly import *
 | 
			
		||||
from utils.nolog import *; logstart('Slang')
 | 
			
		||||
 | 
			
		||||
def debug_compile(src, filename='<string>'):
 | 
			
		||||
def compile(src, filename='<string>', *, compiler):
 | 
			
		||||
	try:
 | 
			
		||||
		#print(f"Source: {{\n{S(src).indent()}\n}}\n")
 | 
			
		||||
 | 
			
		||||
@@ -15,46 +13,36 @@ def debug_compile(src, filename='<string>'):
 | 
			
		||||
		#print(f"Tokens:\n{pformat(tl)}\n")
 | 
			
		||||
 | 
			
		||||
		ast = build_ast(tl, filename)
 | 
			
		||||
		#print(f"Code: {repr(ast.code)}\n")
 | 
			
		||||
		#print(f"Code: {ast.code}\n")
 | 
			
		||||
 | 
			
		||||
		#print(f"Nodes: {pformat(list(walk_ast_nodes(ast)))}\n")
 | 
			
		||||
 | 
			
		||||
		optimize_ast(ast, validate_ast(ast))
 | 
			
		||||
		#print(f"Optimized: {repr(ast.code)}\n")
 | 
			
		||||
		#print(f"Optimized: {ast.code}\n")
 | 
			
		||||
 | 
			
		||||
		code = PyssemblyCompiler.compile_ast(ast, validate_ast(ast), filename=filename)
 | 
			
		||||
		code = compiler.compile_ast(ast, validate_ast(ast), filename=filename)
 | 
			
		||||
		#print("Compiled.\n")
 | 
			
		||||
 | 
			
		||||
		#print("Running.\n")
 | 
			
		||||
		#exec(code, {})
 | 
			
		||||
		#print("\nFinished.\n")
 | 
			
		||||
	except (SlSyntaxError, SlValidationError, SlCompilationError) as ex:
 | 
			
		||||
		ex.line = src.split('\n')[ex.lineno-1]
 | 
			
		||||
		if (not ex.line): ex.line = src.split('\n')[ex.lineno-1]
 | 
			
		||||
		sys.exit(ex)
 | 
			
		||||
	except pyssembly.PyssemblyError as ex:
 | 
			
		||||
		print('Error:', ex)
 | 
			
		||||
		try: code = ex.code.to_code()
 | 
			
		||||
		except pyssembly.PyssemblyError: pass
 | 
			
		||||
		else:
 | 
			
		||||
			print("\nHere is full pyssembly code 'til the errorneous line:\n")
 | 
			
		||||
			dis.dis(code)
 | 
			
		||||
		sys.exit(1)
 | 
			
		||||
	else: return code
 | 
			
		||||
	return code
 | 
			
		||||
 | 
			
		||||
@apmain
 | 
			
		||||
@aparg('file', metavar='<file>', type=argparse.FileType('r'))
 | 
			
		||||
@aparg('-o', dest='output')
 | 
			
		||||
@aparg('-f', dest='compiler', required=True)
 | 
			
		||||
def main(cargs):
 | 
			
		||||
	if (cargs.o is None and not cargs.file.name.rpartition('.')[0]):
 | 
			
		||||
		argparser.add_argument('-o', metavar='<output>', required=True)
 | 
			
		||||
	if (cargs.output is None and not cargs.file.name.rpartition('.')[0]):
 | 
			
		||||
		argparser.add_argument('-o', dest='output', required=True)
 | 
			
		||||
		cargs = argparser.parse_args()
 | 
			
		||||
	src = cargs.file.read()
 | 
			
		||||
	filename = cargs.file.name
 | 
			
		||||
	code = debug_compile(src, filename=filename.join('""'))
 | 
			
		||||
	open(cargs.o or cargs.file.name.rpartition('.')[0]+'.pyc', 'wb').write(pyssembly.asm(code))
 | 
			
		||||
	_cns = importlib.import_module('.compilers.'+cargs.compiler, package=__package__).__dict__.values()
 | 
			
		||||
	compiler = first(i for i in allsubclasses(Compiler) if i in _cns)
 | 
			
		||||
	code = compile(src, filename=filename.join('""'), compiler=compiler)
 | 
			
		||||
	open(cargs.output or cargs.file.name.rpartition('.')[0]+compiler.ext, 'wb').write(code)
 | 
			
		||||
 | 
			
		||||
if (__name__ == '__main__'):
 | 
			
		||||
	argparser.add_argument('file', metavar='<file>', type=argparse.FileType('r'))
 | 
			
		||||
	argparser.add_argument('-o', metavar='<output>')
 | 
			
		||||
	cargs = argparser.parse_args()
 | 
			
		||||
	logstarted(); exit(main(cargs))
 | 
			
		||||
if (__name__ == '__main__'): exit(main())
 | 
			
		||||
else: logimported()
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								TODO.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								TODO.md
									
									
									
									
									
								
							@@ -1 +1,14 @@
 | 
			
		||||
- arrays
 | 
			
		||||
- doc `import [[<namespace>:][<path>/]<pkg>:]<name>`
 | 
			
		||||
- allow `?` modifier only if ≥2 args ?
 | 
			
		||||
- https://esolangs.org/wiki/Stlang
 | 
			
		||||
- https://esolangs.org/wiki/Object_disoriented
 | 
			
		||||
- https://esolangs.org/wiki/Funciton ?
 | 
			
		||||
- `O.each { code }` (i.e. `O.each({code})`)
 | 
			
		||||
- Proposal: `if`, `for` and `while` without brackets
 | 
			
		||||
- OOP
 | 
			
		||||
- Increments
 | 
			
		||||
- FIXME: whitespace in srclength
 | 
			
		||||
- renew all `__repr__`s *AND* `__str__`s
 | 
			
		||||
- Proposal: `f(kw: arg)` = `f(kw=arg)`
 | 
			
		||||
- Proposal: [https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/operators/default]
 | 
			
		||||
- FIXME: `auto' rettype
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								asm/hw.asm
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								asm/hw.asm
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
bits 64
 | 
			
		||||
 | 
			
		||||
;section	.rodata
 | 
			
		||||
hw: db "hw", 0
 | 
			
		||||
 | 
			
		||||
.text
 | 
			
		||||
global _start
 | 
			
		||||
extern puts
 | 
			
		||||
_start:
 | 
			
		||||
	push	rbp
 | 
			
		||||
	mov	rbp, rsp
 | 
			
		||||
	lea	rdi, [hw+rip]
 | 
			
		||||
	call	puts
 | 
			
		||||
	mov	rax, 0
 | 
			
		||||
	pop	rbp
 | 
			
		||||
	ret
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# Slang compilers
 | 
			
		||||
 | 
			
		||||
import abc
 | 
			
		||||
from ..ast import SlSyntaxError, SlValidationError
 | 
			
		||||
import abc, traceback
 | 
			
		||||
 | 
			
		||||
class Compiler(abc.ABC):
 | 
			
		||||
	@abc.abstractclassmethod
 | 
			
		||||
@@ -23,9 +24,10 @@ class SlCompilationError(Exception):
 | 
			
		||||
	def __str__(self):
 | 
			
		||||
		l, line = lstripcount(self.line.partition('\n')[0].replace('\t', ' '), ' \t')
 | 
			
		||||
		offset = (self.node.offset-l) if (self.node.offset != -1) else len(line)
 | 
			
		||||
		return (f'\033[2m(in {self.scope})\033[0m ' if (self.scope is not None) else '')+f"Compilation error: {self.desc}{self.at}"+(':\n'+\
 | 
			
		||||
			'  \033[1m'+line[:offset]+'\033[91m'+line[offset:]+'\033[0m\n'+\
 | 
			
		||||
			'  '+' '*offset+'\033[95m^'+'~'*(self.node.length-1) if (line) else '')
 | 
			
		||||
		return (f'\033[2m(in {self.scope})\033[0m ' if (self.scope is not None) else '') + f"Compilation error: {self.desc}{self.at}"+(':\n' + \
 | 
			
		||||
			'  \033[1m'+line[:offset]+'\033[91m'+line[offset:]+'\033[0m\n' + \
 | 
			
		||||
			'  '+' '*offset+'\033[95m^'+'~'*(self.node.length-1)+'\033[0m' if (line) else '') + \
 | 
			
		||||
			(f"\n\n\033[1;95mCaused by:\033[0m\n{self.__cause__ if (isinstance(self.__cause__, (SlSyntaxError, SlValidationError, SlCompilationError))) else ' '+str().join(traceback.format_exception(type(self.__cause__), self.__cause__, self.__cause__.__traceback__))}" if (self.__cause__ is not None) else '')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def at(self):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										135
									
								
								compilers/esolang/gibberish.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								compilers/esolang/gibberish.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,135 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# Slang Esolang Gibberish compilation target (PoC)
 | 
			
		||||
# https://esolangs.org/wiki/Gibberish_(programming_language)
 | 
			
		||||
 | 
			
		||||
from .. import *
 | 
			
		||||
from Slang.ast import *
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
class Instrs:
 | 
			
		||||
	binopmap = {
 | 
			
		||||
		'+': b'ea',
 | 
			
		||||
		'-': b'es',
 | 
			
		||||
		'*': b'em',
 | 
			
		||||
		'/': b'ed',
 | 
			
		||||
		'<<': b'fl',
 | 
			
		||||
		'>>': b'fr',
 | 
			
		||||
		'&': b'ga',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@init_defaults
 | 
			
		||||
	@autocast
 | 
			
		||||
	def __init__(self, ns, stack: Slist, functions: dict):
 | 
			
		||||
		self.ns, self.stack, self.functions = ns, stack, functions
 | 
			
		||||
		self.code = bytearray()
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTRootNode):
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTCodeNode):
 | 
			
		||||
		for i in x.nodes:
 | 
			
		||||
			self.add(i)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTVardefNode):
 | 
			
		||||
		if (x.value is not None):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.stack[-1] = Signature.build(x, self.ns)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTAssignmentNode):
 | 
			
		||||
		sig = Signature.build(x.name, self.ns)
 | 
			
		||||
		for ii, i in enumerate(self.stack):
 | 
			
		||||
			if (i == sig): self.stack[ii] = None
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.stack[-1] = sig
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFunccallNode):
 | 
			
		||||
		self.load(x)
 | 
			
		||||
		self.code += b'ev'
 | 
			
		||||
		self.stack.pop()
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFuncdefNode):
 | 
			
		||||
		code_ns = self.ns.derive(x.name.identifier)
 | 
			
		||||
		if (x.name.identifier == 'main'):
 | 
			
		||||
			assert not x.argdefs
 | 
			
		||||
			name = x.name.identifier
 | 
			
		||||
		else: name = f"{x.name.identifier}__{self.ns.signatures[x.name.identifier].call.index(CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs)))}"
 | 
			
		||||
		f_instrs = Instrs(ns=code_ns, stack=(Signature.build(i, code_ns) for i in x.argdefs), functions=self.functions.copy())
 | 
			
		||||
		f_instrs.add(x.code)
 | 
			
		||||
		#dlog(f"{x.__fsig__()} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
		self.functions[name] = f_instrs
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTKeywordExprNode):
 | 
			
		||||
		if (x.keyword.keyword == 'return'):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
		else: raise NotImplementedError(x.keyword)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTLiteralNode):
 | 
			
		||||
		s = str(eval(str(x.literal))).encode().split(b']')
 | 
			
		||||
		if (s):
 | 
			
		||||
			s[0] = b'['+s[0]
 | 
			
		||||
			s[-1] += b']'
 | 
			
		||||
		self.code += b'][93]eigtec['.join(s) + b'c'*(len(s)-1)
 | 
			
		||||
		if (issubclass(literal_type(x.literal), (int, float))): self.code += b'ei'
 | 
			
		||||
		self.stack.append(None)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTIdentifierNode):
 | 
			
		||||
		dlog(self.stack)
 | 
			
		||||
		sig = Signature.build(x, self.ns)
 | 
			
		||||
		i = self.stack.rindex(sig)
 | 
			
		||||
		self.code += (b'[%d]eip' if (i >= 10) else b'%dep') % i
 | 
			
		||||
		self.stack.append(sig)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTValueNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTFunccallNode):
 | 
			
		||||
		assert not (x.callargs.starargs or x.callkwargs.callkwargs or x.callkwargs.starkwargs)
 | 
			
		||||
		if (x.callable.value.identifier == 'print'):
 | 
			
		||||
			for i in x.callargs.callargs[:-1]:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				self.code += b'eq[32]eigteq'
 | 
			
		||||
				self.stack.pop()
 | 
			
		||||
			if (x.callargs.callargs):
 | 
			
		||||
				self.load(x.callargs.callargs[-1])
 | 
			
		||||
				self.stack.pop()
 | 
			
		||||
			else: self.code += b'[]'
 | 
			
		||||
			self.code += b'eo'
 | 
			
		||||
			self.stack.append(None)
 | 
			
		||||
			return
 | 
			
		||||
		for i in x.callargs.callargs[::-1]:
 | 
			
		||||
			self.load(i)
 | 
			
		||||
			self.stack.pop()
 | 
			
		||||
		self.code += self.functions[f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}" if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures) else x.callable.value.identifier].code#.join((b'{', b'}'))
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTBinaryExprNode):
 | 
			
		||||
		self.load(x.lvalue)
 | 
			
		||||
		self.load(x.rvalue)
 | 
			
		||||
		self.code += self.binopmap[x.operator.operator]
 | 
			
		||||
		self.stack.pop()
 | 
			
		||||
		self.stack.pop()
 | 
			
		||||
		self.stack.append(None)
 | 
			
		||||
 | 
			
		||||
class GibberishCompiler(Compiler):
 | 
			
		||||
	ext = '.gib'
 | 
			
		||||
 | 
			
		||||
	@classmethod
 | 
			
		||||
	def compile_ast(cls, ast, ns, *, filename):
 | 
			
		||||
		instrs = Instrs(ns=ns)
 | 
			
		||||
		instrs.add(ast)
 | 
			
		||||
		code = bytes(instrs.code)
 | 
			
		||||
		dlog("Code:\n"+code.decode())
 | 
			
		||||
		return code
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
@@ -1,278 +0,0 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# Slang Pyssembly compiler target
 | 
			
		||||
 | 
			
		||||
import pyssembly
 | 
			
		||||
from . import *
 | 
			
		||||
from Slang.ast import *
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
class Instrs:
 | 
			
		||||
	unopmap = {
 | 
			
		||||
		'+': 'POS',
 | 
			
		||||
		'-': 'NEG',
 | 
			
		||||
		'!': 'NOT',
 | 
			
		||||
		'~': 'INV',
 | 
			
		||||
		'not': 'NOT',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	binopmap = {
 | 
			
		||||
		'+': 'ADD',
 | 
			
		||||
		'-': 'SUB',
 | 
			
		||||
		'*': 'MUL',
 | 
			
		||||
		'/': 'DIV',
 | 
			
		||||
		'//': 'FLOORDIV',
 | 
			
		||||
		'%': 'MOD',
 | 
			
		||||
		'**': 'POW',
 | 
			
		||||
		'<<': 'LSHIFT',
 | 
			
		||||
		'>>': 'RSHIFT',
 | 
			
		||||
		'&': 'AND',
 | 
			
		||||
		'|': 'OR',
 | 
			
		||||
		'^': 'XOR',
 | 
			
		||||
		'and': 'AND',
 | 
			
		||||
		'or': 'OR',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@init_defaults
 | 
			
		||||
	def __init__(self, *, name, ns, filename, argdefs=()):
 | 
			
		||||
		self.name, self.ns, self.filename = name, ns, filename
 | 
			
		||||
		self.instrs = list()
 | 
			
		||||
		self.consts = list()
 | 
			
		||||
		self.argnames = list()
 | 
			
		||||
		for i in argdefs:
 | 
			
		||||
			if (i.modifier is not None): raise SlCompilationError("argument modifiers are not supported yet", i.modifier)
 | 
			
		||||
			self.argnames.append(i.name.identifier)
 | 
			
		||||
		self.cellvars = self.argnames.copy()
 | 
			
		||||
		self.srclnotab = list()
 | 
			
		||||
 | 
			
		||||
	def compile(self):
 | 
			
		||||
		return pyssembly.Code('\n'.join(self.instrs), name=self.name, filename=self.filename.strip('"'), srclnotab=self.srclnotab, consts=self.consts, argnames=self.argnames)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTRootNode):
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTCodeNode):
 | 
			
		||||
		lastln = int()
 | 
			
		||||
		for i in x.nodes:
 | 
			
		||||
			self.srclnotab.append(i.lineno-lastln)
 | 
			
		||||
			lastln = i.lineno
 | 
			
		||||
			l = len(self.instrs)
 | 
			
		||||
			self.add(i)
 | 
			
		||||
			self.srclnotab += [0]*(len(self.instrs)-l-1)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTValueNode):
 | 
			
		||||
		self.add(x.value)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTVardefNode):
 | 
			
		||||
		if (x.value is not None):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.store(x.name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTAssignmentNode):
 | 
			
		||||
		if (x.inplace_operator is not None): self.instrs.append(f"LOAD	({x.name})")
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		if (x.inplace_operator is not None): self.instrs.append(f"INP_{self.binopmap[x.inplace_operator.operator]}")
 | 
			
		||||
		self.store(x.name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFunccallNode):
 | 
			
		||||
		self.load(x)
 | 
			
		||||
		self.instrs.append("POP")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTBlockNode):
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFuncdefNode):
 | 
			
		||||
		code_ns = self.ns.derive(x.name.identifier)
 | 
			
		||||
		name = f"{x.name.identifier}__{self.ns.signatures[x.name.identifier].call.index(CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs)))}"
 | 
			
		||||
		f_instrs = Instrs(name=f"{self.name}.{name}", ns=code_ns, filename=self.filename, argdefs=x.argdefs)
 | 
			
		||||
		f_instrs.add(x.code)
 | 
			
		||||
		#dlog(f"{x.__fsig__()} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
		self.consts.append(f_instrs.compile().to_code())
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			f"LOAD	{len(self.consts)-1}",
 | 
			
		||||
			f"LOAD	('{self.name}.{name}')",
 | 
			
		||||
			f"MAKE_FUNCTION	0", # TODO: flags
 | 
			
		||||
		]
 | 
			
		||||
		self.store(name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTKeywordExprNode):
 | 
			
		||||
		if (x.keyword.keyword == 'import'):
 | 
			
		||||
			ns, _, name = x.value.identifier.partition('::')
 | 
			
		||||
			assert ns == 'py'
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"LOAD	(0)", # TODO
 | 
			
		||||
				"LOAD	(None)", # TODO
 | 
			
		||||
			]
 | 
			
		||||
			self.instrs.append(f"IMPORT_NAME	({name})")
 | 
			
		||||
			self.store(name)
 | 
			
		||||
		elif (x.keyword.keyword == 'return'):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.instrs.append("RET")
 | 
			
		||||
		else: raise NotImplementedError(x.keyword)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTConditionalNode):
 | 
			
		||||
		self.load(x.condition)
 | 
			
		||||
		self.instrs.append("JPOPF	:else")
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JUMPF	:end",
 | 
			
		||||
			":else",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTForLoopNode):
 | 
			
		||||
		self.load(x.iterable)
 | 
			
		||||
		#self.cellvars.append(x.name.identifier) # TODO FIXME
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"SETUP_LOOP	:end",
 | 
			
		||||
			"ITER",
 | 
			
		||||
			":for",
 | 
			
		||||
			"FOR	:else",
 | 
			
		||||
		]
 | 
			
		||||
		self.store(x.name)
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JUMPA	:for",
 | 
			
		||||
			":else",
 | 
			
		||||
			"POP_BLOCK",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTWhileLoopNode):
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"SETUP_LOOP	:end",
 | 
			
		||||
			":while",
 | 
			
		||||
		]
 | 
			
		||||
		self.load(x.condition)
 | 
			
		||||
		self.instrs.append("JPOPF	:else")
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JUMPA	:while",
 | 
			
		||||
			":else",
 | 
			
		||||
			"POP_BLOCK",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTElseClauseNode):
 | 
			
		||||
		assert (self.instrs[-1] == ":end")
 | 
			
		||||
		popped = [self.instrs.pop()]
 | 
			
		||||
		if (self.instrs[-1] == "POP_BLOCK"): popped.append(self.instrs.pop())
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
		self.instrs += reversed(popped)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTLiteralNode):
 | 
			
		||||
		self.instrs.append(f"LOAD	({x.literal})")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTIdentifierNode):
 | 
			
		||||
		self.load(x.identifier)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTValueNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTFunccallNode):
 | 
			
		||||
		if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
 | 
			
		||||
			self.load(f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}")
 | 
			
		||||
		else: self.load(x.callable)
 | 
			
		||||
		n = int()
 | 
			
		||||
 | 
			
		||||
		for i in x.callargs.callargs:
 | 
			
		||||
			self.load(i)
 | 
			
		||||
			n += 1
 | 
			
		||||
		if (x.callargs.starargs):
 | 
			
		||||
			if (n):
 | 
			
		||||
				self.instrs.append(f"BUILD_TUPLE	{n}")
 | 
			
		||||
				n = 1
 | 
			
		||||
			for i in x.callargs.starargs:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				n += 1
 | 
			
		||||
			self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL	{n}")
 | 
			
		||||
			n = 0
 | 
			
		||||
 | 
			
		||||
		for i in x.callkwargs.callkwargs:
 | 
			
		||||
			self.load(f"'{i[0]}'")
 | 
			
		||||
			self.load(i[1])
 | 
			
		||||
			n += 1
 | 
			
		||||
		if (n and (x.callargs.starargs or x.callkwargs.starkwargs)):
 | 
			
		||||
			self.instrs.append(f"BUILD_MAP	{n}")
 | 
			
		||||
			n = 1
 | 
			
		||||
		if (x.callkwargs.starkwargs):
 | 
			
		||||
			for i in x.callkwargs.starkwargs:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				n += 1
 | 
			
		||||
			self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL	{n}")
 | 
			
		||||
			n = 1
 | 
			
		||||
 | 
			
		||||
		self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''}	{n}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTAttrgetNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		assert x.optype.special == '.' # TODO
 | 
			
		||||
		self.instrs.append(f"GETATTR	({x.attr})")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTUnaryExprNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.instrs.append(self.unopmap[x.operator.operator])
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTBinaryExprNode):
 | 
			
		||||
		self.load(x.lvalue)
 | 
			
		||||
		char = isinstance(Signature.build(x.lvalue, self.ns), stdlib.char)
 | 
			
		||||
		if (char): self.instrs.append("CALL	(ord) 1")
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		self.load(x.rvalue)
 | 
			
		||||
		if (char and isinstance(Signature.build(x.rvalue, self.ns), stdlib.char)): self.instrs.append("CALL	(ord) 1")
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		if (x.operator.operator == 'to'): self.instrs.append("CALL	(range) 2")
 | 
			
		||||
		else: self.instrs.append(self.binopmap[x.operator.operator])
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		if (char and x.operator.operator not in keyword_operators): self.instrs.append("CALL	(chr) 1")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTItemgetNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.load(x.key)
 | 
			
		||||
		self.instrs.append("SUBSCR")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: str):
 | 
			
		||||
		self.instrs.append(f"LOAD	{f'${x}' if (x in self.cellvars) else f'({x})'}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def store(self, x: ASTIdentifierNode):
 | 
			
		||||
		self.store(x.identifier)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def store(self, x: str):
 | 
			
		||||
		self.instrs.append(f"STORE	{f'${x}' if (x in self.cellvars) else f'({x})'}")
 | 
			
		||||
 | 
			
		||||
class PyssemblyCompiler(Compiler):
 | 
			
		||||
	@classmethod
 | 
			
		||||
	def compile_ast(cls, ast, ns, *, filename):
 | 
			
		||||
		instrs = Instrs(name='<module>', ns=ns, filename=filename)
 | 
			
		||||
		instrs.add(ast)
 | 
			
		||||
		#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
 | 
			
		||||
		code = instrs.compile().to_code()
 | 
			
		||||
		#dis.show_code(code)
 | 
			
		||||
		#dis.dis(code)
 | 
			
		||||
		#print()
 | 
			
		||||
		return code
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
							
								
								
									
										486
									
								
								compilers/pyssembly/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								compilers/pyssembly/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,486 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# Slang Pyssembly compiler target
 | 
			
		||||
 | 
			
		||||
import pyssembly
 | 
			
		||||
from . import std
 | 
			
		||||
from .. import *
 | 
			
		||||
from ...ast import *
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
class Instrs:
 | 
			
		||||
	unopmap = {
 | 
			
		||||
		'+': 'POS',
 | 
			
		||||
		'-': 'NEG',
 | 
			
		||||
		'!': 'NOT',
 | 
			
		||||
		'~': 'INV',
 | 
			
		||||
		'not': 'NOT',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	binopmap = {
 | 
			
		||||
		'+': 'ADD',
 | 
			
		||||
		'-': 'SUB',
 | 
			
		||||
		'*': 'MUL',
 | 
			
		||||
		'/': 'DIV',
 | 
			
		||||
		'//': 'IDIV',
 | 
			
		||||
		'%': 'MOD',
 | 
			
		||||
		'**': 'POW',
 | 
			
		||||
		'<<': 'LSHIFT',
 | 
			
		||||
		'>>': 'RSHIFT',
 | 
			
		||||
		'&': 'AND',
 | 
			
		||||
		'|': 'OR',
 | 
			
		||||
		'^': 'XOR',
 | 
			
		||||
		'and': 'AND',
 | 
			
		||||
		'or': 'OR',
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_self_argdef = ASTArgdefNode(Class, ASTIdentifierNode('<self>', lineno=None, offset=None), None, None, lineno=None, offset=None)
 | 
			
		||||
 | 
			
		||||
	def _class_init(self, *args, **kwargs):
 | 
			
		||||
		getattr(self, '<init>')()
 | 
			
		||||
		getattr(self, f"<constructor ({', '.join(type(i).__name__ for i in (*args, *kwargs.values()))})>")(*args, **kwargs)
 | 
			
		||||
	_class_init = _class_init.__code__.replace(co_name='<new>')
 | 
			
		||||
 | 
			
		||||
	@init_defaults
 | 
			
		||||
	def __init__(self, *, name, ns, filename, argdefs=(), lastnodens: lambda: [None, None], firstlineno=0):
 | 
			
		||||
		self.name, self.ns, self.filename, self.lastnodens, self.firstlineno = name, ns, filename, lastnodens, firstlineno
 | 
			
		||||
		self.instrs = list()
 | 
			
		||||
		self.consts = list()
 | 
			
		||||
		self.argnames = list()
 | 
			
		||||
		for i in argdefs:
 | 
			
		||||
			if (i.modifier is not None): raise NotImplementedError("argument modifiers are not supported yet")
 | 
			
		||||
			self.argnames.append(i.name.identifier)
 | 
			
		||||
		self.cellvars = self.argnames.copy()
 | 
			
		||||
		self.srclnotab = list()
 | 
			
		||||
		self.lastln = self.firstlineno
 | 
			
		||||
		self._class_init = self._class_init.replace(co_filename=self.filename, co_consts=tuple(i.replace(co_filename=self.filename) if (isinstance(i, CodeType)) else i for i in self._class_init.co_consts))
 | 
			
		||||
 | 
			
		||||
	def compile(self):
 | 
			
		||||
		return pyssembly.Code('\n'.join(self.instrs), name=self.name, filename=self.filename.strip('"'), srclnotab=self.srclnotab, firstlineno=self.firstlineno, consts=self.consts, argnames=self.argnames)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTRootNode):
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTCodeNode):
 | 
			
		||||
		for ii, i in enumerate(x.nodes):
 | 
			
		||||
			assert (i.lineno >= self.lastln)
 | 
			
		||||
			if (i.lineno != self.lastln): self.instrs.append(f"#line {i.lineno}")
 | 
			
		||||
			self.lastln = i.lineno
 | 
			
		||||
			self.lastnodens[0] = i
 | 
			
		||||
			self.add(i)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTValueNode):
 | 
			
		||||
		self.add(x.value)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTVardefNode):
 | 
			
		||||
		typesig = Signature.build(x.type, self.ns)
 | 
			
		||||
		if (x.value is not None):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.store(x.name)
 | 
			
		||||
		elif (isinstance(typesig, Class)):
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"LOAD		(object)",
 | 
			
		||||
				"GETATTR	(__new__)",
 | 
			
		||||
			]
 | 
			
		||||
			self.load(x.type.type)
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"CALL		1",
 | 
			
		||||
				"DUP",
 | 
			
		||||
				"GETATTR	<<init>>",
 | 
			
		||||
				"CALL",
 | 
			
		||||
				"POP",
 | 
			
		||||
			]
 | 
			
		||||
			self.store(x.name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTAssignmentNode):
 | 
			
		||||
		if (x.inplace_operator is not None): self.instrs.append(f"LOAD	({x.name})")
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		if (x.inplace_operator is not None): self.instrs.append(f"IP{self.binopmap[x.inplace_operator.operator]}")
 | 
			
		||||
		if (x.isattr):
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"LOAD		$<self>",
 | 
			
		||||
				f"SETATTR	({x.name})",
 | 
			
		||||
			]
 | 
			
		||||
		else: self.store(x.name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTUnpackAssignmentNode):
 | 
			
		||||
		assert (x.inplace_operator is None) # TODO
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.instrs.append(f"UNPACK	{len(x.names)}")
 | 
			
		||||
		for name in x.names:
 | 
			
		||||
			self.store(name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTAttrsetNode):
 | 
			
		||||
		assert (x.assignment.isattr)
 | 
			
		||||
		if (x.assignment.inplace_operator is not None):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"DUP",
 | 
			
		||||
				"GETATTR	({x.assignment.name})",
 | 
			
		||||
			]
 | 
			
		||||
			self.load(x.assignment.value)
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				f"IP{self.binopmap[x.assignment.inplace_operator.operator]}",
 | 
			
		||||
				"ROT",
 | 
			
		||||
			]
 | 
			
		||||
		else:
 | 
			
		||||
			self.load(x.assignment.value)
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
		self.instrs.append(f"SETATTR	({x.assignment.name})")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFunccallNode):
 | 
			
		||||
		self.load(x)
 | 
			
		||||
		self.instrs.append("POP")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTBlockNode):
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFuncdefNode):
 | 
			
		||||
		code_ns = self.ns.derive(x.name.identifier)
 | 
			
		||||
		name = f"{x.name.identifier}({CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs))})"
 | 
			
		||||
		self.lastnodens[1] = code_ns
 | 
			
		||||
		fname = f"{self.name}.<{x.__fsig__()}>"
 | 
			
		||||
		f_instrs = Instrs(name=fname, ns=code_ns, filename=self.filename, argdefs=x.argdefs, lastnodens=self.lastnodens, firstlineno=x.lineno)
 | 
			
		||||
		f_instrs.add(x.code)
 | 
			
		||||
		#dlog(f"{fname} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
		self.consts.append(f_instrs.compile().to_code())
 | 
			
		||||
		self.lastnodens[1] = self.ns
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			f"LOAD		{len(self.consts)-1}",
 | 
			
		||||
			f"LOAD		('{fname}')",
 | 
			
		||||
			"MKFUNC		0", # TODO: flags
 | 
			
		||||
		]
 | 
			
		||||
		self.store(name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTClassdefNode):
 | 
			
		||||
		code_ns = self.ns.derive(x.name.identifier)
 | 
			
		||||
		name = x.name.identifier
 | 
			
		||||
		self.lastnodens[1] = code_ns
 | 
			
		||||
		cname = str(name)
 | 
			
		||||
		c_instrs = Instrs(name=cname, ns=code_ns, filename=self.filename, lastnodens=self.lastnodens, firstlineno=x.lineno)
 | 
			
		||||
		c_instrs.consts.append(self._class_init)
 | 
			
		||||
		c_instrs.instrs += [
 | 
			
		||||
			f"LOAD		{len(self.consts)-1}",
 | 
			
		||||
			f"LOAD		('<new>')",
 | 
			
		||||
			"MKFUNC		0", # TODO: flags
 | 
			
		||||
		]
 | 
			
		||||
		c_instrs.store('__init__')
 | 
			
		||||
		c_instrs.add(x.code)
 | 
			
		||||
		self.consts.append(c_instrs.compile().to_code())
 | 
			
		||||
		self.lastnodens[1] = self.ns
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"LOAD_BUILD_CLASS",
 | 
			
		||||
			f"LOAD		{len(self.consts)-1}",
 | 
			
		||||
			f"LOAD		('{cname}')",
 | 
			
		||||
			"MKFUNC		0", # TODO: flags?
 | 
			
		||||
			f"LOAD		('{cname}')",
 | 
			
		||||
			"CALL		2",
 | 
			
		||||
		]
 | 
			
		||||
		self.store(name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTKeywordExprNode):
 | 
			
		||||
		if (x.keyword.keyword == 'import'):
 | 
			
		||||
			m = re.fullmatch(r'(?:(?:(\w+):)?(?:([\w./]+)/)?([\w.]+):)?([\w*]+)', x.value.identifier)
 | 
			
		||||
			assert (m is not None)
 | 
			
		||||
			namespace, path, pkg, name = m.groups()
 | 
			
		||||
			if (namespace is None): namespace = 'sl'
 | 
			
		||||
			if (path is None): path = '.'
 | 
			
		||||
			if (pkg is None): pkg = name
 | 
			
		||||
			if (namespace == 'py'):
 | 
			
		||||
				assert (path == '.')
 | 
			
		||||
				self.instrs += [
 | 
			
		||||
					"LOAD		(0)", # TODO
 | 
			
		||||
					"LOAD		(())", # TODO
 | 
			
		||||
					f"IMPORT	({pkg})",
 | 
			
		||||
				]
 | 
			
		||||
				if (name == '*'): self.instrs.append("IMPALL")
 | 
			
		||||
				else:
 | 
			
		||||
					if (name != pkg): self.instrs.append("IMPFROM")
 | 
			
		||||
					self.store(name)
 | 
			
		||||
			elif (namespace == 'sl'):
 | 
			
		||||
				pkg = pkg.replace('.', '/')
 | 
			
		||||
				filename = f"{os.path.join(path, pkg)}.sl"
 | 
			
		||||
				src = open(filename, 'r').read()
 | 
			
		||||
				tl = parse_string(src)
 | 
			
		||||
				ast = build_ast(tl, filename)
 | 
			
		||||
				optimize_ast(ast, validate_ast(ast))
 | 
			
		||||
				instrs = Instrs(name='<module>', ns=validate_ast(ast), filename=filename)
 | 
			
		||||
				instrs.add(ast)
 | 
			
		||||
				code = instrs.compile().to_code()
 | 
			
		||||
				# TODO
 | 
			
		||||
			else: raise WTFException(namespace)
 | 
			
		||||
		elif (x.keyword.keyword == 'return'):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.instrs.append("RET")
 | 
			
		||||
		elif (x.keyword.keyword == 'delete'):
 | 
			
		||||
			self.delete(x.value)
 | 
			
		||||
		elif (x.keyword.keyword == 'break'):
 | 
			
		||||
			self.instrs.append("JF :end")
 | 
			
		||||
		else: raise NotImplementedError(x.keyword)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTKeywordDefNode):
 | 
			
		||||
		name = x.name.identifier
 | 
			
		||||
		if (x.keyword.keyword == 'main'):
 | 
			
		||||
			code_ns = self.ns.derive(name)
 | 
			
		||||
			self.lastnodens[1] = code_ns
 | 
			
		||||
			f_instrs = Instrs(name=name, ns=code_ns, filename=self.filename, lastnodens=self.lastnodens, firstlineno=x.lineno)
 | 
			
		||||
			f_instrs.add(x.code)
 | 
			
		||||
			#dlog(f"{name} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
			self.consts.append(f_instrs.compile().to_code())
 | 
			
		||||
			self.lastnodens[1] = self.ns
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"LOAD		(__name__)",
 | 
			
		||||
				"LOAD		('__main__')",
 | 
			
		||||
				"CMP		(==)",
 | 
			
		||||
				"JFP		:nomain",
 | 
			
		||||
				f"LOAD		{len(self.consts)-1}",
 | 
			
		||||
				f"LOAD		('{name}')",
 | 
			
		||||
				"MKFUNC		0",
 | 
			
		||||
				"CALL		0",
 | 
			
		||||
				"POP",
 | 
			
		||||
				":nomain",
 | 
			
		||||
			]
 | 
			
		||||
		elif (x.keyword.keyword == 'init'):
 | 
			
		||||
			code_ns = self.ns.derive(name)
 | 
			
		||||
			self.lastnodens[1] = code_ns
 | 
			
		||||
			f_instrs = Instrs(name=name, argdefs=(self._self_argdef,), ns=code_ns, filename=self.filename, lastnodens=self.lastnodens, firstlineno=x.lineno)
 | 
			
		||||
			f_instrs.add(x.code)
 | 
			
		||||
			#dlog(f"{name} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
			self.consts.append(f_instrs.compile().to_code())
 | 
			
		||||
			self.lastnodens[1] = self.ns
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				f"LOAD		{len(self.consts)-1}",
 | 
			
		||||
				f"LOAD		('{name}')",
 | 
			
		||||
				"MKFUNC		0",
 | 
			
		||||
			]
 | 
			
		||||
			self.store(name)
 | 
			
		||||
		elif (x.keyword.keyword == 'constr'):
 | 
			
		||||
			code_ns = self.ns.derive(name)
 | 
			
		||||
			self.ns.define(x, redefine=True)
 | 
			
		||||
			self.lastnodens[1] = code_ns
 | 
			
		||||
			name = f"<constructor ({S(', ').join(i.type for i in x.argdefs)})>"
 | 
			
		||||
			f_instrs = Instrs(name=name, ns=code_ns, filename=self.filename, argdefs=(self._self_argdef, *x.argdefs), lastnodens=self.lastnodens, firstlineno=x.lineno)
 | 
			
		||||
			f_instrs.add(x.code)
 | 
			
		||||
			#dlog(f"{name} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
			self.consts.append(f_instrs.compile().to_code())
 | 
			
		||||
			self.lastnodens[1] = self.ns
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				f"LOAD		{len(self.consts)-1}",
 | 
			
		||||
				f"LOAD		('{name}')",
 | 
			
		||||
				"MKFUNC		0", # TODO: flags
 | 
			
		||||
			]
 | 
			
		||||
			self.store(name)
 | 
			
		||||
		else: raise NotImplementedError(x.keyword)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTConditionalNode):
 | 
			
		||||
		self.load(x.condition)
 | 
			
		||||
		self.instrs.append("JFP	:else")
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JF	:end",
 | 
			
		||||
			":else",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTForLoopNode):
 | 
			
		||||
		self.load(x.iterable)
 | 
			
		||||
		#self.cellvars.append(x.name.identifier) # TODO FIXME
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"ITER",
 | 
			
		||||
			":for",
 | 
			
		||||
			"FOR	:else",
 | 
			
		||||
		]
 | 
			
		||||
		self.store(x.name)
 | 
			
		||||
		self.add(x.code) # TODO: stack effect (py38)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JA	:for",
 | 
			
		||||
			":else",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTWhileLoopNode):
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			":while",
 | 
			
		||||
		]
 | 
			
		||||
		self.load(x.condition)
 | 
			
		||||
		self.instrs.append("JFP	:else")
 | 
			
		||||
		self.add(x.code) # TODO: stack effect (py38)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JA	:while",
 | 
			
		||||
			":else",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTElseClauseNode):
 | 
			
		||||
		ii = -1 - self.instrs[-1].startswith('#line')
 | 
			
		||||
		assert (self.instrs.pop(ii) == ":end")
 | 
			
		||||
		self.add(x.code) # TODO: stack effect (py38)
 | 
			
		||||
		self.instrs.append(":end")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTLiteralNode):
 | 
			
		||||
		self.instrs.append(f"LOAD	({x.literal})")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTIdentifierNode):
 | 
			
		||||
		self.load(x.identifier)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTListNode):
 | 
			
		||||
		for i in x.values:
 | 
			
		||||
			self.load(i)
 | 
			
		||||
		self.instrs.append(f"BUILD_LIST	{len(x.values)}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTTupleNode):
 | 
			
		||||
		for i in x.values:
 | 
			
		||||
			self.load(i)
 | 
			
		||||
		self.instrs.append(f"BUILD_TUPLE	{len(x.values)}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTValueNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTFunccallNode):
 | 
			
		||||
		if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures and not isinstance(self.ns.signatures[x.callable.value.identifier], Class)):
 | 
			
		||||
			self.load(f"{x.callable.value.identifier}({CallArguments.build(x, self.ns)})")
 | 
			
		||||
		else: self.load(x.callable)
 | 
			
		||||
		n = int()
 | 
			
		||||
 | 
			
		||||
		for i in x.callargs.callargs:
 | 
			
		||||
			self.load(i)
 | 
			
		||||
			n += 1
 | 
			
		||||
		if (x.callargs.starargs):
 | 
			
		||||
			if (n):
 | 
			
		||||
				self.instrs.append(f"BUILD_TUPLE	{n}")
 | 
			
		||||
				n = 1
 | 
			
		||||
			for i in x.callargs.starargs:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				n += 1
 | 
			
		||||
			self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL	{n}")
 | 
			
		||||
			n = 0
 | 
			
		||||
 | 
			
		||||
		for i in x.callkwargs.callkwargs:
 | 
			
		||||
			self.load(f"'{i[0]}'")
 | 
			
		||||
			self.load(i[1])
 | 
			
		||||
			n += 1
 | 
			
		||||
		if (n and (x.callargs.starargs or x.callkwargs.starkwargs)):
 | 
			
		||||
			self.instrs.append(f"BUILD_MAP	{n}")
 | 
			
		||||
			n = 1
 | 
			
		||||
		if (x.callkwargs.starkwargs):
 | 
			
		||||
			for i in x.callkwargs.starkwargs:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				n += 1
 | 
			
		||||
			self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL	{n}")
 | 
			
		||||
			n = 1
 | 
			
		||||
 | 
			
		||||
		self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''}	{n}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTAttrgetNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		assert (x.optype.special == '.') # TODO
 | 
			
		||||
		self.instrs.append(f"GETATTR	({x.attr})")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTUnaryExprNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.instrs.append(self.unopmap[x.operator.operator])
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTBinaryExprNode):
 | 
			
		||||
		self.load(x.lvalue)
 | 
			
		||||
		char = isinstance(Signature.build(x.lvalue, self.ns), stdlib.char)
 | 
			
		||||
		if (char): self.instrs.append("CALL	(ord)	1")
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		self.load(x.rvalue)
 | 
			
		||||
		if (char and isinstance(Signature.build(x.rvalue, self.ns), stdlib.char)): self.instrs.append("CALL	(ord)	1")
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		if (x.operator.operator == 'to'): self.instrs.append("CALL	(range)	2")
 | 
			
		||||
		else: self.instrs.append(f"CMP	({x.operator.operator})" if (x.operator.operator in dis.cmp_op) else self.binopmap[x.operator.operator])
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		if (char and x.operator.operator not in logical_operators): self.instrs.append("CALL	(chr)	1")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTItemgetNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.load(x.key)
 | 
			
		||||
		self.instrs.append("SUBSCR")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: str):
 | 
			
		||||
		self.instrs.append(f"LOAD	{f'${x}' if (x in self.cellvars) else f'<{x}>'}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x):
 | 
			
		||||
		self.instrs.append(f"LOAD_CONST	({repr(x)})")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def store(self, x: ASTIdentifierNode):
 | 
			
		||||
		self.store(x.identifier)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def store(self, x: str):
 | 
			
		||||
		self.instrs.append(f"STORE	{f'${x}' if (x in self.cellvars) else f'<{x}>'}")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def delete(self, x: ASTIdentifierNode):
 | 
			
		||||
		self.delete(x.identifier)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def delete(self, x: str):
 | 
			
		||||
		self.instrs.append(f"DELETE	{f'${x}' if (x in self.cellvars) else f'<{x}>'}")
 | 
			
		||||
 | 
			
		||||
class PyssemblyCompiler(Compiler):
 | 
			
		||||
	ext = '.pyc'
 | 
			
		||||
 | 
			
		||||
	@classmethod
 | 
			
		||||
	def compile_ast(cls, ast, ns, *, filename):
 | 
			
		||||
		instrs = Instrs(name='<module>', ns=ns, filename=filename)
 | 
			
		||||
 | 
			
		||||
		instrs.consts.append(compile(open(std.__file__).read(), '<std>', 'exec'))
 | 
			
		||||
		instrs.instrs += [
 | 
			
		||||
			f"LOAD	{len(instrs.consts)-1}",
 | 
			
		||||
			"LOAD	('<std>')",
 | 
			
		||||
			"MKFUNC	0", # TODO?
 | 
			
		||||
			"CALL",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
		try: instrs.add(ast)
 | 
			
		||||
		except Exception as ex: raise SlCompilationError('Compilation error', instrs.lastnodens[0], scope=instrs.lastnodens[1].scope) from ex
 | 
			
		||||
 | 
			
		||||
		#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
 | 
			
		||||
 | 
			
		||||
		try:
 | 
			
		||||
			code = instrs.compile().to_code()
 | 
			
		||||
			#dis.show_code(code); dis.dis(code); print()
 | 
			
		||||
			code = pyssembly.asm(code)
 | 
			
		||||
		except pyssembly.PyssemblyError as ex:
 | 
			
		||||
			print('Error:', ex)
 | 
			
		||||
			try: code = ex.code.to_code()
 | 
			
		||||
			except pyssembly.PyssemblyError: pass
 | 
			
		||||
			else:
 | 
			
		||||
				print("\nHere is full pyssembly code 'til the errorneous line:\n")
 | 
			
		||||
				dis.dis(code)
 | 
			
		||||
			raise SlCompilationError('Pyssembly error', ast, scope=instrs.ns) from ex
 | 
			
		||||
 | 
			
		||||
		return code
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
							
								
								
									
										2
									
								
								compilers/pyssembly/std.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								compilers/pyssembly/std.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
class stdio:
 | 
			
		||||
	println = print
 | 
			
		||||
							
								
								
									
										1
									
								
								compilers/pyssembly/typeshed
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								compilers/pyssembly/typeshed
									
									
									
									
									
										Submodule
									
								
							 Submodule compilers/pyssembly/typeshed added at b44cd294c4
									
								
							
							
								
								
									
										247
									
								
								compilers/sbc.py
									
									
									
									
									
								
							
							
						
						
									
										247
									
								
								compilers/sbc.py
									
									
									
									
									
								
							@@ -6,7 +6,11 @@ from Slang.ast import *
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
NOP	= 0x00
 | 
			
		||||
RET	= 0x01
 | 
			
		||||
END	= 0x01
 | 
			
		||||
POP	= 0x02
 | 
			
		||||
RET	= 0x03
 | 
			
		||||
BLTIN	= 0x04
 | 
			
		||||
CODE	= 0x05
 | 
			
		||||
 | 
			
		||||
POS	= 0x10
 | 
			
		||||
NEG	= 0x11
 | 
			
		||||
@@ -24,15 +28,28 @@ ADD	= 0x20
 | 
			
		||||
SUB	= 0x21
 | 
			
		||||
MUL	= 0x22
 | 
			
		||||
DIV	= 0x23
 | 
			
		||||
FLRDIV	= 0x24
 | 
			
		||||
IDIV	= 0x24
 | 
			
		||||
MOD	= 0x25
 | 
			
		||||
POW	= 0x26
 | 
			
		||||
SHL	= 0x27
 | 
			
		||||
SHR	= 0x28
 | 
			
		||||
LSH	= 0x27
 | 
			
		||||
RSH	= 0x28
 | 
			
		||||
AND	= 0x29
 | 
			
		||||
OR	= 0x2a
 | 
			
		||||
XOR	= 0x2b
 | 
			
		||||
 | 
			
		||||
EQ	= 0x30
 | 
			
		||||
NE	= 0x31
 | 
			
		||||
LT	= 0x32
 | 
			
		||||
GT	= 0x33
 | 
			
		||||
LE	= 0x34
 | 
			
		||||
GE	= 0x35
 | 
			
		||||
IS	= 0x36
 | 
			
		||||
ISNOT	= 0x37
 | 
			
		||||
 | 
			
		||||
IF	= 0x40
 | 
			
		||||
ELSE	= 0x41
 | 
			
		||||
EXEC	= 0x42
 | 
			
		||||
 | 
			
		||||
ALLOC	= 0xa0
 | 
			
		||||
EXTEND	= 0xa1
 | 
			
		||||
CONST	= 0xa2
 | 
			
		||||
@@ -40,26 +57,81 @@ JUMPF	= 0xa3
 | 
			
		||||
JUMPB	= 0xa4
 | 
			
		||||
SCPGET	= 0xa5
 | 
			
		||||
SCPSET	= 0xa6
 | 
			
		||||
CALL	= 0xa7
 | 
			
		||||
 | 
			
		||||
HASARG	= 0xa0
 | 
			
		||||
 | 
			
		||||
def readVarInt(s):
 | 
			
		||||
	r = int()
 | 
			
		||||
	i = int()
 | 
			
		||||
	while (True):
 | 
			
		||||
		b = s.recv(1)[0]
 | 
			
		||||
		r |= (b & (1 << 7)-1) << (7*i)
 | 
			
		||||
		if (not b & (1 << 7)): break
 | 
			
		||||
		i += 1
 | 
			
		||||
	return r
 | 
			
		||||
 | 
			
		||||
def writeVarInt(v):
 | 
			
		||||
	assert v >= 0
 | 
			
		||||
	r = bytearray()
 | 
			
		||||
	while (True):
 | 
			
		||||
		c = v & (1 << 7)-1
 | 
			
		||||
		v >>= 7
 | 
			
		||||
		if (v): c |= (1 << 7)
 | 
			
		||||
		r.append(c)
 | 
			
		||||
		if (not v): break
 | 
			
		||||
	return bytes(r)
 | 
			
		||||
 | 
			
		||||
class Instrs:
 | 
			
		||||
	unops = '+-!~'
 | 
			
		||||
	binops = (*'+-*/%', '**', '<<', '>>', '&', '|', '^')
 | 
			
		||||
	unopmap = {
 | 
			
		||||
		'+': POS,
 | 
			
		||||
		'-': NEG,
 | 
			
		||||
		'!': NOT,
 | 
			
		||||
		'~': INV,
 | 
			
		||||
		'not': NOT,
 | 
			
		||||
	}
 | 
			
		||||
	binopmap = {
 | 
			
		||||
		'+': ADD,
 | 
			
		||||
		'-': SUB,
 | 
			
		||||
		'*': MUL,
 | 
			
		||||
		'/': DIV,
 | 
			
		||||
		'//': IDIV,
 | 
			
		||||
		'%': MOD,
 | 
			
		||||
		'**': POW,
 | 
			
		||||
		'<<': LSH,
 | 
			
		||||
		'>>': RSH,
 | 
			
		||||
		'&': AND,
 | 
			
		||||
		'|': OR,
 | 
			
		||||
		'^': XOR,
 | 
			
		||||
 | 
			
		||||
		'==': EQ,
 | 
			
		||||
		'!=': NE,
 | 
			
		||||
		'<': LT,
 | 
			
		||||
		'>': GT,
 | 
			
		||||
		'<=': LE,
 | 
			
		||||
		'>=': GE,
 | 
			
		||||
		'is': IS,
 | 
			
		||||
		'is not': ISNOT,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@init_defaults
 | 
			
		||||
	def __init__(self, *, name, ns, filename, argdefs=()):
 | 
			
		||||
		self.ns = ns
 | 
			
		||||
	def __init__(self, *, name, ns, filename, scpcells: indexset):
 | 
			
		||||
		self.name, self.ns, self.filename, self.scpcells = name, ns, filename, scpcells
 | 
			
		||||
		self.instrs = bytearray()
 | 
			
		||||
 | 
			
		||||
	def compile(self):
 | 
			
		||||
		raise TODO
 | 
			
		||||
		return bytes(self.instrs)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, opcode: int, oparg: int = None):
 | 
			
		||||
	def add(self, opcode: lambda x: isinstance(x, int) and x < HASARG):
 | 
			
		||||
		self.instrs.append(opcode)
 | 
			
		||||
		if (opcode >= HASARG): self.instrs.append(oparg)
 | 
			
		||||
		else: assert oparg is None
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, opcode: lambda x: isinstance(x, int) and x >= HASARG, oparg: int):
 | 
			
		||||
		self.instrs.append(opcode)
 | 
			
		||||
		self.instrs.append(oparg)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTRootNode):
 | 
			
		||||
@@ -67,7 +139,6 @@ class Instrs:
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTCodeNode):
 | 
			
		||||
		lastln = int()
 | 
			
		||||
		for i in x.nodes:
 | 
			
		||||
			self.add(i)
 | 
			
		||||
 | 
			
		||||
@@ -83,15 +154,14 @@ class Instrs:
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTAssignmentNode):
 | 
			
		||||
		if (x.inplace_operator is not None): self.instrs.append(f"LOAD	({x.name})")
 | 
			
		||||
		if (x.inplace_operator is not None): raise NotImplementedError()
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		if (x.inplace_operator is not None): self.instrs.append(f"INP_{self.binopmap[x.inplace_operator.operator]}")
 | 
			
		||||
		self.store(x.name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTFunccallNode):
 | 
			
		||||
		self.load(x)
 | 
			
		||||
		self.instrs.append("POP")
 | 
			
		||||
		self.add(POP)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTBlockNode):
 | 
			
		||||
@@ -101,46 +171,52 @@ class Instrs:
 | 
			
		||||
	def add(self, x: ASTFuncdefNode):
 | 
			
		||||
		code_ns = self.ns.derive(x.name.identifier)
 | 
			
		||||
		name = f"{x.name.identifier}__{self.ns.signatures[x.name.identifier].call.index(CallArguments(args=tuple(Signature.build(i, code_ns) for i in x.argdefs)))}"
 | 
			
		||||
		f_instrs = Instrs(name=f"{self.name}.{name}", ns=code_ns, filename=self.filename, argdefs=x.argdefs)
 | 
			
		||||
		fname = f"{self.name}.<{x.__fsig__()}>"
 | 
			
		||||
		self.scpcells[name]
 | 
			
		||||
		f_instrs = Instrs(name=fname, ns=code_ns, filename=self.filename, scpcells=self.scpcells.copy())
 | 
			
		||||
		for i in x.argdefs:
 | 
			
		||||
			f_instrs.scpcells[i.name.identifier]
 | 
			
		||||
		f_instrs.add(x.code)
 | 
			
		||||
		#dlog(f"{x.__fsig__()} instrs:\n"+'\n'.join(f_instrs.instrs)+'\n')
 | 
			
		||||
		self.consts.append(f_instrs.compile().to_code())
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			f"LOAD	{len(self.consts)-1}",
 | 
			
		||||
			f"LOAD	('{self.name}.{name}')",
 | 
			
		||||
			f"MAKE_FUNCTION	0", # TODO: flags
 | 
			
		||||
		]
 | 
			
		||||
		self.add(CODE)
 | 
			
		||||
		self.instrs += f_instrs.instrs
 | 
			
		||||
		self.add(END)
 | 
			
		||||
		self.store(name+'.len')
 | 
			
		||||
		self.store(name)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTKeywordExprNode):
 | 
			
		||||
		if (x.keyword.keyword == 'import'):
 | 
			
		||||
			ns, _, name = x.value.identifier.partition('::')
 | 
			
		||||
			assert ns == 'py'
 | 
			
		||||
			self.instrs += [
 | 
			
		||||
				"LOAD	(0)", # TODO
 | 
			
		||||
				"LOAD	(None)", # TODO
 | 
			
		||||
			]
 | 
			
		||||
			self.instrs.append(f"IMPORT_NAME	({name})")
 | 
			
		||||
			raise TODO
 | 
			
		||||
			self.store(name)
 | 
			
		||||
		elif (x.keyword.keyword == 'return'):
 | 
			
		||||
			self.load(x.value)
 | 
			
		||||
			self.instrs.append("RET")
 | 
			
		||||
			self.add(RET)
 | 
			
		||||
		else: raise NotImplementedError(x.keyword)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTKeywordDefNode):
 | 
			
		||||
		if (x.keyword.keyword == 'main'):
 | 
			
		||||
			name = '<main>'
 | 
			
		||||
			code_ns = self.ns.derive(name)
 | 
			
		||||
			f_instrs = Instrs(name=name, ns=code_ns, filename=self.filename)
 | 
			
		||||
			f_instrs.add(x.code)
 | 
			
		||||
			self.add(CODE)
 | 
			
		||||
			self.instrs += f_instrs.instrs
 | 
			
		||||
			self.add(END)
 | 
			
		||||
			self.add(EXEC)
 | 
			
		||||
			self.add(POP)
 | 
			
		||||
		else: raise NotImplementedError(x.keyword)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTConditionalNode):
 | 
			
		||||
		self.load(x.condition)
 | 
			
		||||
		self.instrs.append("JPOPF	:else")
 | 
			
		||||
		self.add(IF)
 | 
			
		||||
		self.add(x.code)
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"JUMPF	:end",
 | 
			
		||||
			":else",
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
		self.add(END)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTForLoopNode):
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	def add_(self, x: ASTForLoopNode):
 | 
			
		||||
		self.load(x.iterable)
 | 
			
		||||
		#self.cellvars.append(x.name.identifier) # TODO FIXME
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
@@ -158,8 +234,8 @@ class Instrs:
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTWhileLoopNode):
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	def add_(self, x: ASTWhileLoopNode):
 | 
			
		||||
		self.instrs += [
 | 
			
		||||
			"SETUP_LOOP	:end",
 | 
			
		||||
			":while",
 | 
			
		||||
@@ -174,8 +250,8 @@ class Instrs:
 | 
			
		||||
			":end",
 | 
			
		||||
		]
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def add(self, x: ASTElseClauseNode):
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	def add_(self, x: ASTElseClauseNode):
 | 
			
		||||
		assert (self.instrs[-1] == ":end")
 | 
			
		||||
		popped = [self.instrs.pop()]
 | 
			
		||||
		if (self.instrs[-1] == "POP_BLOCK"): popped.append(self.instrs.pop())
 | 
			
		||||
@@ -184,7 +260,16 @@ class Instrs:
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTLiteralNode):
 | 
			
		||||
		self.instrs.append(f"LOAD	({x.literal})")
 | 
			
		||||
		sig = Signature.build(x, ns=self.ns)
 | 
			
		||||
		if (hasattr(sig, 'fmt')):
 | 
			
		||||
			v = struct.pack(sig.fmt, int(x.literal))
 | 
			
		||||
		elif (isinstance(sig, stdlib.int)):
 | 
			
		||||
			v = writeVarInt(int(x.literal))
 | 
			
		||||
		elif (isinstance(sig, stdlib.str)):
 | 
			
		||||
			v = x.literal.encode('utf8')
 | 
			
		||||
		else: raise NotImplementedError(sig)
 | 
			
		||||
		self.add(CONST, len(v))
 | 
			
		||||
		self.instrs += v
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTIdentifierNode):
 | 
			
		||||
@@ -196,42 +281,29 @@ class Instrs:
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTFunccallNode):
 | 
			
		||||
		if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
 | 
			
		||||
			self.load(f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}")
 | 
			
		||||
		else: self.load(x.callable)
 | 
			
		||||
		n = int()
 | 
			
		||||
		nargs = int()
 | 
			
		||||
 | 
			
		||||
		for i in x.callargs.callargs:
 | 
			
		||||
			self.load(i)
 | 
			
		||||
			n += 1
 | 
			
		||||
		if (x.callargs.starargs):
 | 
			
		||||
			if (n):
 | 
			
		||||
				self.instrs.append(f"BUILD_TUPLE	{n}")
 | 
			
		||||
				n = 1
 | 
			
		||||
			for i in x.callargs.starargs:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				n += 1
 | 
			
		||||
			self.instrs.append(f"BUILD_TUPLE_UNPACK_WITH_CALL	{n}")
 | 
			
		||||
			n = 0
 | 
			
		||||
			nargs += 1
 | 
			
		||||
		if (x.callargs.starargs): raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
		for i in x.callkwargs.callkwargs:
 | 
			
		||||
			self.load(f"'{i[0]}'")
 | 
			
		||||
			self.load(i[1])
 | 
			
		||||
			n += 1
 | 
			
		||||
		if (n and (x.callargs.starargs or x.callkwargs.starkwargs)):
 | 
			
		||||
			self.instrs.append(f"BUILD_MAP	{n}")
 | 
			
		||||
			n = 1
 | 
			
		||||
		if (x.callkwargs.starkwargs):
 | 
			
		||||
			for i in x.callkwargs.starkwargs:
 | 
			
		||||
				self.load(i)
 | 
			
		||||
				n += 1
 | 
			
		||||
			self.instrs.append(f"BUILD_MAP_UNPACK_WITH_CALL	{n}")
 | 
			
		||||
			n = 1
 | 
			
		||||
		if (x.callkwargs.callkwargs): raise NotImplementedError()
 | 
			
		||||
		if (x.callkwargs.starkwargs): raise NotImplementedError()
 | 
			
		||||
 | 
			
		||||
		self.instrs.append(f"CALL{'EX' if (x.callargs.starargs or x.callkwargs.starkwargs) else 'KW' if (x.callkwargs.callkwargs) else ''}	{n}")
 | 
			
		||||
		if (isinstance(x.callable, ASTValueNode) and isinstance(x.callable.value, ASTIdentifierNode) and x.callable.value.identifier in self.ns.signatures):
 | 
			
		||||
			name = f"{x.callable.value.identifier}__{self.ns.signatures[x.callable.value.identifier].call.index(CallArguments.build(x, self.ns))}"
 | 
			
		||||
			self.load(name)
 | 
			
		||||
			self.load(name+'.len')
 | 
			
		||||
			self.add(EXEC)
 | 
			
		||||
		else:  # builtin
 | 
			
		||||
			#self.add(ITOA) # TODO FIXME cast
 | 
			
		||||
			self.add(BLTIN)
 | 
			
		||||
			self.instrs += x.callable.value.identifier.encode('ascii')+b'\0'
 | 
			
		||||
			self.add(CALL, nargs)
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTAttrgetNode):
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	def load_(self, x: ASTAttrgetNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		assert x.optype.special == '.' # TODO
 | 
			
		||||
		self.instrs.append(f"GETATTR	({x.attr})")
 | 
			
		||||
@@ -239,31 +311,24 @@ class Instrs:
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTUnaryExprNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.instrs.append(self.unopmap[x.operator.operator])
 | 
			
		||||
		self.add(self.unopmap[x.operator.operator])
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTBinaryExprNode):
 | 
			
		||||
		self.load(x.lvalue)
 | 
			
		||||
		char = isinstance(Signature.build(x.lvalue, self.ns), stdlib.char)
 | 
			
		||||
		if (char): self.instrs.append("CALL	(ord) 1")
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		self.load(x.rvalue)
 | 
			
		||||
		if (char and isinstance(Signature.build(x.rvalue, self.ns), stdlib.char)): self.instrs.append("CALL	(ord) 1")
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		if (x.operator.operator == 'to'): self.instrs.append("CALL	(range) 2")
 | 
			
		||||
		else: self.instrs.append(self.binopmap[x.operator.operator])
 | 
			
		||||
		if (x.operator.operator == 'xor'): self.instrs.append("BOOL")
 | 
			
		||||
		if (char and x.operator.operator not in keyword_operators): self.instrs.append("CALL	(chr) 1")
 | 
			
		||||
		if (x.operator.operator == 'to'): raise NotImplementedError()
 | 
			
		||||
		else: self.add(self.binopmap[x.operator.operator])
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: ASTItemgetNode):
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	def load_(self, x: ASTItemgetNode):
 | 
			
		||||
		self.load(x.value)
 | 
			
		||||
		self.load(x.key)
 | 
			
		||||
		self.instrs.append("SUBSCR")
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def load(self, x: str):
 | 
			
		||||
		self.instrs.append(f"LOAD	{f'${x}' if (x in self.cellvars) else f'({x})'}")
 | 
			
		||||
		self.add(SCPGET, self.scpcells[x])
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def store(self, x: ASTIdentifierNode):
 | 
			
		||||
@@ -271,18 +336,16 @@ class Instrs:
 | 
			
		||||
 | 
			
		||||
	@dispatch
 | 
			
		||||
	def store(self, x: str):
 | 
			
		||||
		self.instrs.append(f"STORE	{f'${x}' if (x in self.cellvars) else f'({x})'}")
 | 
			
		||||
		self.add(SCPSET, self.scpcells[x])
 | 
			
		||||
 | 
			
		||||
class SBCCompiler(Compiler):
 | 
			
		||||
	ext = '.sbc'
 | 
			
		||||
 | 
			
		||||
class PyssemblyCompiler(Compiler):
 | 
			
		||||
	@classmethod
 | 
			
		||||
	def compile_ast(cls, ast, ns, *, filename):
 | 
			
		||||
		instrs = Instrs(name='<module>', ns=ns, filename=filename)
 | 
			
		||||
		instrs.add(ast)
 | 
			
		||||
		#dlog("Instrs:\n"+'\n'.join(instrs.instrs)+'\n')
 | 
			
		||||
		code = instrs.compile().to_code()
 | 
			
		||||
		#dis.show_code(code)
 | 
			
		||||
		#dis.dis(code)
 | 
			
		||||
		#print()
 | 
			
		||||
		code = instrs.compile()
 | 
			
		||||
		return code
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										21
									
								
								lexer.py
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								lexer.py
									
									
									
									
									
								
							@@ -13,21 +13,30 @@ def read_token(src, *, lineno, offset, lineoff):
 | 
			
		||||
		if (isinstance(r, int) and r <= 0): length = max(length, -r); continue
 | 
			
		||||
		n, s = r if (isinstance(r, tuple)) else (r, src[:r])
 | 
			
		||||
		return (offset+n, Token(ii, s, lineno=lineno, offset=offset+lineoff))
 | 
			
		||||
	else: raise SlSyntaxError("Invalid token", line, lineno=lineno, offset=offset+lineoff, length=length)
 | 
			
		||||
	else: raise SlSyntaxError("Invalid token", line, lineno=lineno, offset=offset+lineoff, length=length+l)
 | 
			
		||||
 | 
			
		||||
def parse_expr(src, *, lineno=1, lineoff=0):
 | 
			
		||||
	r = list()
 | 
			
		||||
	lines = src.count('\n')
 | 
			
		||||
	offset = int()
 | 
			
		||||
	continueln = False
 | 
			
		||||
	while (True):
 | 
			
		||||
		offset, tok = read_token(src, lineno=lineno, offset=offset, lineoff=lineoff)
 | 
			
		||||
		if (tok is None): break
 | 
			
		||||
		offset, tok = read_token(src, lineno=lines-src[offset:].count('\n')+lineno, offset=offset, lineoff=lineoff)
 | 
			
		||||
		if (tok is None):
 | 
			
		||||
			if (not continueln): break
 | 
			
		||||
			continueln = False
 | 
			
		||||
			offset += 1
 | 
			
		||||
			lineoff = -offset
 | 
			
		||||
			continue
 | 
			
		||||
		elif (continueln and tok.token[0] != '#'): raise SlSyntaxError("Expected newline or comment after line continuation", src, lineno=lines-src[offset:].count('\n')+lineno, offset=tok.offset, length=tok.length)
 | 
			
		||||
		r.append(tok)
 | 
			
		||||
		if (tok.token[0] != '#'): continueln = (tok.token == '\\' and tok.offset)
 | 
			
		||||
	return offset, r
 | 
			
		||||
 | 
			
		||||
def parse_string(src):
 | 
			
		||||
def parse_string(src, lnooff=0):
 | 
			
		||||
	src = src.rstrip()
 | 
			
		||||
	tl = list()
 | 
			
		||||
	lines = src.count('\n')
 | 
			
		||||
	lines = src.count('\n')+lnooff
 | 
			
		||||
	lineoff = int()
 | 
			
		||||
	while (src):
 | 
			
		||||
		offset, r = parse_expr(src, lineno=lines-src.count('\n')+1, lineoff=lineoff)
 | 
			
		||||
@@ -39,4 +48,4 @@ def parse_string(src):
 | 
			
		||||
		tl.append(r)
 | 
			
		||||
	return tl
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										179
									
								
								repl.py
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								repl.py
									
									
									
									
									
								
							@@ -4,13 +4,15 @@
 | 
			
		||||
import readline
 | 
			
		||||
from .ast import *
 | 
			
		||||
from .lexer import *
 | 
			
		||||
from utils import *
 | 
			
		||||
from utils.nolog import *
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTCodeNode, ns):
 | 
			
		||||
	r = None
 | 
			
		||||
	for i in node.nodes:
 | 
			
		||||
		if (isinstance(i, ASTElseClauseNode) and r is not None): continue
 | 
			
		||||
		r = execute_node(i, ns)
 | 
			
		||||
		if (r is not None):
 | 
			
		||||
		if (r not in (None, ...)):
 | 
			
		||||
			ns.values['_'] = r
 | 
			
		||||
			print(repr(r))
 | 
			
		||||
	else: return
 | 
			
		||||
@@ -20,13 +22,17 @@ def execute_node(node: ASTCodeNode, ns):
 | 
			
		||||
def execute_node(node: ASTVardefNode, ns):
 | 
			
		||||
	ns.values[node.name] = node.value
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTBlockNode, ns):
 | 
			
		||||
	return execute_node(node.code, ns)
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTFuncdefNode, ns):
 | 
			
		||||
	ns.values[node.name.identifier] = (node.argdefs, node.code)
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTAssignmentNode, ns):
 | 
			
		||||
	ns.values[node.name] = node.value
 | 
			
		||||
	ns.values[node.name] = execute_node(ASTBinaryExprNode(node.name, node.inplace_operator, node.value, lineno=node.lineno, offset=node.offset), ns) if (node.inplace_operator is not None) else node.value
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTFunccallNode, ns):
 | 
			
		||||
@@ -42,48 +48,161 @@ def execute_node(node: ASTValueNode, ns):
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTIdentifierNode, ns):
 | 
			
		||||
	return ns.values[node]
 | 
			
		||||
	try: return ns.values[node]
 | 
			
		||||
	except KeyError: raise SlValidationError(f"{node} is not initialized", node, scope=ns.scope)
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTLiteralNode, ns):
 | 
			
		||||
	return eval(str(node.literal))
 | 
			
		||||
	return eval(node.literal) if (isinstance(node.literal, str)) else node.literal
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTListNode, ns):
 | 
			
		||||
	return node.values
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTKeywordExprNode, ns):
 | 
			
		||||
	if (node.keyword.keyword == 'return'): return execute_node(node.value, ns)
 | 
			
		||||
	raise NotImplementedError(node.keyword)
 | 
			
		||||
	#if (node.keyword.keyword == 'return'): return execute_node(node.value, ns) # TODO FIXME???
 | 
			
		||||
	#el
 | 
			
		||||
	if (node.keyword.keyword == 'delete'): ns.delete(node.value)
 | 
			
		||||
	else: raise NotImplementedError(node.keyword)
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTConditionalNode, ns):
 | 
			
		||||
	if (execute_node(node.condition, ns)):
 | 
			
		||||
		execute_node(node.code, ns)
 | 
			
		||||
	else: return
 | 
			
		||||
	return ...
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTForLoopNode, ns):
 | 
			
		||||
	for i in execute_node(node.iterable, ns):
 | 
			
		||||
		execute_node(node.code, ns)
 | 
			
		||||
	else: return
 | 
			
		||||
	return ...
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTUnaryExprNode, ns):
 | 
			
		||||
	return eval(f"{node.operator.operator} {execute_node(node.value, ns)}")
 | 
			
		||||
	value = execute_node(node.value, ns)
 | 
			
		||||
	return eval(f"{node.operator.operator} value")
 | 
			
		||||
 | 
			
		||||
@dispatch
 | 
			
		||||
def execute_node(node: ASTBinaryExprNode, ns):
 | 
			
		||||
	return eval(f"{execute_node(node.lvalue, ns)} {node.operator.operator} {execute_node(node.rvalue, ns)}")
 | 
			
		||||
	lvalue = execute_node(node.lvalue, ns)
 | 
			
		||||
	rvalue = execute_node(node.rvalue, ns)
 | 
			
		||||
	if (node.operator.operator == 'xor'): return eval("(lvalue and not rvalue) or (rvalue and not lvalue)")
 | 
			
		||||
	elif (node.operator.operator == 'to'): return range(lvalue, rvalue)
 | 
			
		||||
	else: return eval(f"lvalue {node.operator.operator} rvalue")
 | 
			
		||||
 | 
			
		||||
class Completer:
 | 
			
		||||
	def __init__(self, namespace):
 | 
			
		||||
		self.namespace = namespace
 | 
			
		||||
 | 
			
		||||
	def complete(self, text, state):
 | 
			
		||||
		if (state == 0):
 | 
			
		||||
			if ('.' in text): self.matches = self.attr_matches(text)
 | 
			
		||||
			else: self.matches = self.global_matches(text)
 | 
			
		||||
		try: return self.matches[state]
 | 
			
		||||
		except IndexError: return None
 | 
			
		||||
 | 
			
		||||
	def _callable_postfix(self, val, word):
 | 
			
		||||
		if (isinstance(val, ASTCallableNode)): word += '('
 | 
			
		||||
		return word
 | 
			
		||||
 | 
			
		||||
	def global_matches(self, text):
 | 
			
		||||
		matches = list()
 | 
			
		||||
		seen = set()
 | 
			
		||||
		n = len(text)
 | 
			
		||||
 | 
			
		||||
		for word in keywords:
 | 
			
		||||
			if (word[:n] != text): continue
 | 
			
		||||
			seen.add(word)
 | 
			
		||||
			matches.append(word+' ')
 | 
			
		||||
 | 
			
		||||
		for word, val in self.namespace.values.items():
 | 
			
		||||
			if (word[:n] != text or word in seen): continue
 | 
			
		||||
			seen.add(word)
 | 
			
		||||
			matches.append(self._callable_postfix(val, word))
 | 
			
		||||
 | 
			
		||||
		return matches
 | 
			
		||||
 | 
			
		||||
	def attr_matches(self, text):
 | 
			
		||||
		m = re.match(r"(\w+(\.\w+)*)\.(\w*)", text)
 | 
			
		||||
		if (m is None): return ()
 | 
			
		||||
		expr, attr = m.group(1, 3)
 | 
			
		||||
		try: obj = self.namespace.values[expr] # TODO FIXME
 | 
			
		||||
		except KeyError: return ()
 | 
			
		||||
 | 
			
		||||
		words = set() # TODO FIXME attrs
 | 
			
		||||
 | 
			
		||||
		matches = list()
 | 
			
		||||
		n = len(attr)
 | 
			
		||||
		if (attr == ''): noprefix = '_'
 | 
			
		||||
		elif (attr == '_'): noprefix = '__'
 | 
			
		||||
		else: noprefix = None
 | 
			
		||||
 | 
			
		||||
		while (True):
 | 
			
		||||
			for word in words:
 | 
			
		||||
				if (word[:n] != attr or (noprefix and word[:n+1] == noprefix)): continue
 | 
			
		||||
				match = f"{expr}.{word}"
 | 
			
		||||
				try: val = getattr(obj, word)
 | 
			
		||||
				except Exception: pass  # Include even if attribute not set
 | 
			
		||||
				else: match = self._callable_postfix(val, match)
 | 
			
		||||
				matches.append(match)
 | 
			
		||||
			if (matches or not noprefix): break
 | 
			
		||||
			if (noprefix == '_'): noprefix = '__'
 | 
			
		||||
			else: noprefix = None
 | 
			
		||||
 | 
			
		||||
		matches.sort()
 | 
			
		||||
		return matches
 | 
			
		||||
 | 
			
		||||
def repl():
 | 
			
		||||
	ns = Namespace('<repl>')
 | 
			
		||||
	#ns.values['print'] = print
 | 
			
		||||
	ns.define(ASTIdentifierNode('_', lineno=None, offset=None), stdlib.Any())
 | 
			
		||||
 | 
			
		||||
	completer = Completer(ns)
 | 
			
		||||
	histfile = os.path.expanduser('~/.sli_history')
 | 
			
		||||
	try: readline.read_history_file(histfile)
 | 
			
		||||
	except FileNotFoundError: pass
 | 
			
		||||
	for i in (
 | 
			
		||||
		'set colored-completion-prefix on',
 | 
			
		||||
		'set enable-bracketed-paste on',
 | 
			
		||||
		#'set horizontal-scroll-mode on',
 | 
			
		||||
		'set skip-completed-text on',
 | 
			
		||||
		'tab: complete',
 | 
			
		||||
	): readline.parse_and_bind(i)
 | 
			
		||||
	readline.set_completer(completer.complete)
 | 
			
		||||
	#readline.set_completion_display_matches_hook(completer.display) # TODO
 | 
			
		||||
 | 
			
		||||
	l = list()
 | 
			
		||||
	tl = list()
 | 
			
		||||
	while (True):
 | 
			
		||||
		try:
 | 
			
		||||
			l.append(input(f"\1\033[1;93m\2{'...' if (tl) else '>>>'}\1\033[0m\2 "))
 | 
			
		||||
			tll = parse_string(l[-1])
 | 
			
		||||
			if (not tll): continue
 | 
			
		||||
			tl += tll
 | 
			
		||||
			if (tl[-1][-1].token == '\\'): continue
 | 
			
		||||
			if (len(tl) >= 2 and tl[-2][-1].token == '\\'): tl[-1] = tl[-2][:-1]+tl.pop()
 | 
			
		||||
			if (tl[0][-1].token == '{' and tl[-1][-1].token != '}'): continue
 | 
			
		||||
			ast = build_ast(tl, interactive=True)
 | 
			
		||||
			validate_ast(ast, ns)
 | 
			
		||||
			optimize_ast(ast, ns)
 | 
			
		||||
			execute_node(ast.code, ns)
 | 
			
		||||
		except (EOFError, KeyboardInterrupt): break
 | 
			
		||||
		except (SlSyntaxError, SlValidationError) as ex:
 | 
			
		||||
			ex.line = l[ex.lineno-1]
 | 
			
		||||
			print(ex)
 | 
			
		||||
		tl.clear()
 | 
			
		||||
		l.clear()
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
	try:
 | 
			
		||||
		while (True):
 | 
			
		||||
			try:
 | 
			
		||||
				l.append(input(f"\1\033[1;93m\2{'...' if (tl) else '>>>'}\1\033[0m\2 "))
 | 
			
		||||
				tll = parse_string(l[-1], lnooff=len(l)-1)
 | 
			
		||||
				if (not tll): l.pop(); continue
 | 
			
		||||
				tl += tll
 | 
			
		||||
				if (tl[-1][-1].token == '\\'): continue
 | 
			
		||||
				#if (len(tl) >= 2 and tl[-2][-1].token == '\\'): tl[-1] = tl[-2][:-1]+tl.pop() # TODO FIXME?: [['a', '+', '\\'], 'b'] --> [['a', '+', 'b']]
 | 
			
		||||
				if (tl[0][-1].token == '{' and tl[-1][-1].token != '}'): continue
 | 
			
		||||
				ast = build_ast(tl, interactive=True)
 | 
			
		||||
				validate_ast(ast, ns)
 | 
			
		||||
				optimize_ast(ast, ns)
 | 
			
		||||
				execute_node(ast.code, ns)
 | 
			
		||||
			except KeyboardInterrupt:
 | 
			
		||||
				buf = readline.get_line_buffer()
 | 
			
		||||
				print(f"\r\033[2m^C{'> '+buf if (buf) else ' '}\033[0m")
 | 
			
		||||
			except EOFError:
 | 
			
		||||
				print(end='\r\033[K')
 | 
			
		||||
				break
 | 
			
		||||
			except (SlSyntaxError, SlValidationError) as ex:
 | 
			
		||||
				ex.line = l[ex.lineno-1]
 | 
			
		||||
				print(ex)
 | 
			
		||||
			tl.clear()
 | 
			
		||||
			l.clear()
 | 
			
		||||
	finally: readline.write_history_file(histfile)
 | 
			
		||||
 | 
			
		||||
if (__name__ == '__main__'): repl()
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								sl/Slang/Slang.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								sl/Slang/Slang.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
# Slang
 | 
			
		||||
 | 
			
		||||
import lexer:*
 | 
			
		||||
 | 
			
		||||
str src = "main {print('hello');}"
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println("Source: {"+src+"}\n")
 | 
			
		||||
 | 
			
		||||
	list tl = parse_string(src)
 | 
			
		||||
	stdio.println(tl)
 | 
			
		||||
	stdio.println("Tokens:")
 | 
			
		||||
	stdio.println(tl)
 | 
			
		||||
	stdio.println("\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
							
								
								
									
										74
									
								
								sl/Slang/lexer.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								sl/Slang/lexer.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
			
		||||
# Slang lexer
 | 
			
		||||
 | 
			
		||||
import tokens:*
 | 
			
		||||
 | 
			
		||||
tuple read_token(str src, int lineno, int offset, int lineoff) {
 | 
			
		||||
	tuple c = lstripcount(src#|[offset:]|#, whitespace)
 | 
			
		||||
	int l = c[0]
 | 
			
		||||
	src = c[1]
 | 
			
		||||
	str line = src
 | 
			
		||||
	offset += l
 | 
			
		||||
	if not src or src[0] in '\n;' {
 | 
			
		||||
		return (offset, None)
 | 
			
		||||
	}
 | 
			
		||||
	int length = 0
 | 
			
		||||
	int ii = -1
 | 
			
		||||
	for i in Token.types {
 | 
			
		||||
		ii += 1
 | 
			
		||||
		auto r = globals['find_'+i.casefold()](src) or 0
 | 
			
		||||
		if r isof int and r <= 0 {
 | 
			
		||||
			length = max(length, -r)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		tuple c
 | 
			
		||||
		if r isof tuple {
 | 
			
		||||
			c = r
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			c = (r, src[:r])
 | 
			
		||||
		}
 | 
			
		||||
		int n = c[0]
 | 
			
		||||
		str s = c[1]
 | 
			
		||||
		return (offset+n, Token(ii, s, lineno=lineno, offset=offset+lineoff))
 | 
			
		||||
	} #else raise SlSyntaxError("Invalid token", line, lineno=lineno, offset=offset+lineoff, length=length)
 | 
			
		||||
	return (0, Token())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
tuple parse_expr(str src, int lineno = 1, int lineoff = 0) {
 | 
			
		||||
	list r = [Token]
 | 
			
		||||
	int offset
 | 
			
		||||
	while 1 {
 | 
			
		||||
		offset, tok = read_token(src, lineno=lineno, offset=offset, lineoff=lineoff)
 | 
			
		||||
		if tok is None {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		r.append(tok)
 | 
			
		||||
	}
 | 
			
		||||
	return (offset, r)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
list parse_string(str src) {
 | 
			
		||||
	src = src.rstrip()
 | 
			
		||||
	list tl = [Token]
 | 
			
		||||
	int lines = src.count('\n')
 | 
			
		||||
	int lineoff = 0
 | 
			
		||||
	while src {
 | 
			
		||||
		tuple c = parse_expr(src, lineno=lines-src.count('\n')+1, lineoff=lineoff)
 | 
			
		||||
		int offset = c[0]
 | 
			
		||||
		list r = c[1]
 | 
			
		||||
		lineoff += offset
 | 
			
		||||
		if offset < src.len {
 | 
			
		||||
			if src[offset] == '\n' {
 | 
			
		||||
				lineoff = 0
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				lineoff += 1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		src = src[offset+1:]
 | 
			
		||||
		tl.append(r)
 | 
			
		||||
	}
 | 
			
		||||
	return tl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
							
								
								
									
										49
									
								
								sl/Slang/tokens.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								sl/Slang/tokens.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
# Slang tokens
 | 
			
		||||
 | 
			
		||||
str whitespace = ' \t\r\v\f'
 | 
			
		||||
 | 
			
		||||
tuple lstripcount(str s, str chars) {
 | 
			
		||||
	int ii = -1
 | 
			
		||||
	char i
 | 
			
		||||
	for i in s {
 | 
			
		||||
		ii += 1
 | 
			
		||||
		if i not in chars {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		ii = 0
 | 
			
		||||
	}
 | 
			
		||||
	return (ii, s#|[ii:]|#)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class Token {
 | 
			
		||||
	int type
 | 
			
		||||
	str token
 | 
			
		||||
	int lineno
 | 
			
		||||
	int offset
 | 
			
		||||
 | 
			
		||||
	tuple types# = ('SPECIAL', 'OPERATOR', 'LITERAL', 'KEYWORD', 'IDENTIFIER')  # order is also resolution order
 | 
			
		||||
 | 
			
		||||
	constr(int type, str token, int lineno, int offset) {
 | 
			
		||||
		#.type, .token, .lineno, .offset = type, token, lineno, offset
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#|repr {
 | 
			
		||||
		return "<Token {.types[.type]} «{repr(.token)[1:-1]}» at line {.lineno}, offset {.offset}>"
 | 
			
		||||
	}|#
 | 
			
		||||
 | 
			
		||||
	#|eq {
 | 
			
		||||
		#return super() == x or .token == x
 | 
			
		||||
	}|#
 | 
			
		||||
 | 
			
		||||
	#|property typename {
 | 
			
		||||
		#return .types[.type]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	property length {
 | 
			
		||||
		#return .token.length
 | 
			
		||||
	}|#
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
							
								
								
									
										288
									
								
								slang.lang
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								slang.lang
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,288 @@
 | 
			
		||||
<?xml version="1.0" encoding="UTF-8"?>
 | 
			
		||||
 | 
			
		||||
<language id="slang" name="Slang" version="2.0" _section="Script">
 | 
			
		||||
	<metadata>
 | 
			
		||||
		<property name="mimetypes">text/x-slang;application/x-slang</property>
 | 
			
		||||
		<property name="globs">*.sl</property>
 | 
			
		||||
		<property name="line-comment-start">#</property>
 | 
			
		||||
		<property name="block-comment-start">#|</property>
 | 
			
		||||
		<property name="block-comment-start">|#</property>
 | 
			
		||||
	</metadata>
 | 
			
		||||
 | 
			
		||||
	<styles>
 | 
			
		||||
		<style id="comment"		name="Comment"			map-to="def:comment"/>
 | 
			
		||||
		<style id="keyword"		name="Keyword"			map-to="def:keyword"/>
 | 
			
		||||
		<style id="reserved"		name="Reserved Keyword"		map-to="def:reserved"/>
 | 
			
		||||
		<style id="string"		name="String"			map-to="def:string"/>
 | 
			
		||||
		<style id="multiline-string"	name="Multi-line String"	map-to="def:string"/>
 | 
			
		||||
		<style id="character"		name="Character"		map-to="def:character"/>
 | 
			
		||||
		<style id="escaped-char"	name="Escaped Character"	map-to="def:special-char"/>
 | 
			
		||||
		<style id="boolean"		name="Boolean"			map-to="def:boolean"/>
 | 
			
		||||
		<style id="floating-point"	name="Floating point number"	map-to="def:floating-point"/>
 | 
			
		||||
		<style id="decimal"		name="Decimal number"		map-to="def:decimal"/>
 | 
			
		||||
		<style id="base-n-integer"	name="Base-N number"		map-to="def:base-n-integer"/>
 | 
			
		||||
		<style id="complex"		name="Complex number"		map-to="def:complex"/>
 | 
			
		||||
		<style id="builtin-constant"	name="Builtin Constant"		map-to="def:special-constant"/>
 | 
			
		||||
		<style id="builtin-object"	name="Builtin Object"		map-to="def:function"/>
 | 
			
		||||
		<style id="builtin-type"	name="Builtin Type"		map-to="def:type"/>
 | 
			
		||||
		<style id="function-name"	name="Function Name"		map-to="def:function"/>
 | 
			
		||||
		<style id="class-name"		name="Class Name"		map-to="def:function"/>
 | 
			
		||||
		<style id="decorator"		name="Decorator"		map-to="def:preprocessor"/>
 | 
			
		||||
	</styles>
 | 
			
		||||
 | 
			
		||||
	<definitions>
 | 
			
		||||
		<define-regex id="identifier">[_\w][_\w\d]*</define-regex>
 | 
			
		||||
		<define-regex id="number">[1-9][0-9]*</define-regex>
 | 
			
		||||
 | 
			
		||||
		<define-regex id="identifier-path" extended="true">
 | 
			
		||||
			(\%{identifier}\.)*\%{identifier}
 | 
			
		||||
		</define-regex>
 | 
			
		||||
		<define-regex id="relative-path" extended="true">
 | 
			
		||||
			(\.*\%{identifier-path}|\.+)
 | 
			
		||||
		</define-regex>
 | 
			
		||||
 | 
			
		||||
		<context id="comment" style-ref="comment" end-at-line-end="true" class-disabled="no-spell-check" class="comment">
 | 
			
		||||
			<start>#</start>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="def:in-line-comment"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
		<context id="multiline-comment" style-ref="comment" class-disabled="no-spell-check" class="comment">
 | 
			
		||||
			<start>#\|</start>
 | 
			
		||||
			<end>\|#</end>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="def:in-comment"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
		<context id="close-comment-outside-comment" style-ref="error">
 | 
			
		||||
			<match>\|#(?!#\|)</match>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="escaped-char" style-ref="escaped-char" extend-parent="true">
 | 
			
		||||
			<match extended="true">
 | 
			
		||||
				\\(								 # leading backslash
 | 
			
		||||
				[\\'"abfnrtv]		 | # single escaped char
 | 
			
		||||
				N\{[0-9A-Z\ -]+\} | # named unicode character
 | 
			
		||||
				u[0-9A-Fa-f]{4}	 | # xxxx - character with 16-bit hex value xxxx
 | 
			
		||||
				U[0-9A-Fa-f]{8}	 | # xxxxxxxx - character with 32-bit hex value xxxxxxxx
 | 
			
		||||
				x[0-9A-Fa-f]{1,2} | # \xhh - character with hex value hh
 | 
			
		||||
				[0-7]{1,3}					# \ooo - character with octal value ooo
 | 
			
		||||
				)
 | 
			
		||||
			</match>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="multiline-double-quoted-string" style-ref="string" class="string" class-disabled="no-spell-check">
 | 
			
		||||
			<start>"""</start>
 | 
			
		||||
			<end>"""</end>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="escaped-char"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="multiline-single-quoted-string" style-ref="multiline-string" class="string" class-disabled="no-spell-check">
 | 
			
		||||
			<start>'''</start>
 | 
			
		||||
			<end>'''</end>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="escaped-char"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="double-quoted-string" style-ref="string" end-at-line-end="true" class="string" class-disabled="no-spell-check">
 | 
			
		||||
			<start>"</start>
 | 
			
		||||
			<end>"</end>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="escaped-char"/>
 | 
			
		||||
				<context ref="def:line-continue"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="single-quoted-string" style-ref="string" end-at-line-end="true" class="string" class-disabled="no-spell-check">
 | 
			
		||||
			<start>'</start>
 | 
			
		||||
			<end>'</end>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="escaped-char"/>
 | 
			
		||||
				<context ref="def:line-continue"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="char" style-ref="character" class="string" class-disabled="no-spell-check">
 | 
			
		||||
			<match extended="true">'([^'])'</match>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="boolean" style-ref="boolean">
 | 
			
		||||
			<prefix>(?<![\w\.])</prefix>
 | 
			
		||||
			<keyword>false</keyword>
 | 
			
		||||
			<keyword>true</keyword>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<define-regex id="float" extended="true">
 | 
			
		||||
			( (\d+)?\.\d+ | \d+\. ) |
 | 
			
		||||
			( (\d+|(\d+)?\.\d+|\d+\.)[eE][+-]?\d+ )
 | 
			
		||||
		</define-regex>
 | 
			
		||||
 | 
			
		||||
		<context id="complex" style-ref="complex">
 | 
			
		||||
			<match>(?<![\w\.])(\%{float}|\d+)[jJ]\b</match>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="float" style-ref="floating-point">
 | 
			
		||||
			<match>(?<![\w\.])\%{float}(?![\w\.])</match>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="function-definition">
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="class-definition">
 | 
			
		||||
			<match extended="true">
 | 
			
		||||
				(class)
 | 
			
		||||
				\s+
 | 
			
		||||
				(\%{identifier})
 | 
			
		||||
			</match>
 | 
			
		||||
			<include>
 | 
			
		||||
				<context sub-pattern="1" style-ref="keyword"/>
 | 
			
		||||
				<context sub-pattern="2" style-ref="class-name"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="decorator" style-ref="decorator">
 | 
			
		||||
			<match>@\%{identifier-path}</match>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="keywords" style-ref="keyword">
 | 
			
		||||
			<keyword>if</keyword>
 | 
			
		||||
			<keyword>for</keyword>
 | 
			
		||||
			<keyword>in</keyword>
 | 
			
		||||
			<keyword>while</keyword>
 | 
			
		||||
			<keyword>else</keyword>
 | 
			
		||||
			<keyword>class</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>return</keyword>
 | 
			
		||||
			<keyword>break</keyword>
 | 
			
		||||
			<keyword>continue</keyword>
 | 
			
		||||
			<keyword>import</keyword>
 | 
			
		||||
			<keyword>delete</keyword>
 | 
			
		||||
			<keyword>assert</keyword>
 | 
			
		||||
			<keyword>breakpoint</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>main</keyword>
 | 
			
		||||
			<keyword>exit</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>init</keyword>
 | 
			
		||||
			<keyword>constr</keyword>
 | 
			
		||||
			<keyword>repr</keyword>
 | 
			
		||||
			<keyword>eq</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>const</keyword>
 | 
			
		||||
			<keyword>static</keyword>
 | 
			
		||||
			<keyword>volatile</keyword>
 | 
			
		||||
			<keyword>property</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>is</keyword>
 | 
			
		||||
			<keyword>in</keyword>
 | 
			
		||||
			<keyword>not</keyword>
 | 
			
		||||
			<keyword>and</keyword>
 | 
			
		||||
			<keyword>but</keyword>
 | 
			
		||||
			<keyword>xor</keyword>
 | 
			
		||||
			<keyword>or</keyword>
 | 
			
		||||
			<keyword>isof</keyword>
 | 
			
		||||
			<keyword>to</keyword>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="reserved-keywords" style-ref="reserved">
 | 
			
		||||
			<keyword>def</keyword>
 | 
			
		||||
			<keyword>try</keyword>
 | 
			
		||||
			<keyword>catch</keyword>
 | 
			
		||||
			<keyword>except</keyword>
 | 
			
		||||
			<keyword>finally</keyword>
 | 
			
		||||
			<keyword>raise</keyword>
 | 
			
		||||
			<keyword>with</keyword>
 | 
			
		||||
			<keyword>yield</keyword>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="builtin-constants" style-ref="builtin-constant">
 | 
			
		||||
			<prefix>(?<![\w\.])</prefix>
 | 
			
		||||
			<keyword>none</keyword>
 | 
			
		||||
			<keyword>null</keyword>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="builtin-objects" style-ref="builtin-object">
 | 
			
		||||
			<prefix>(?<![\w\.])</prefix>
 | 
			
		||||
			<keyword>stdio</keyword>
 | 
			
		||||
			<keyword>globals</keyword>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="builtin-types" style-ref="builtin-type">
 | 
			
		||||
			<prefix>(?<![\w\.])</prefix>
 | 
			
		||||
			<keyword>i8</keyword>
 | 
			
		||||
			<keyword>i16</keyword>
 | 
			
		||||
			<keyword>i32</keyword>
 | 
			
		||||
			<keyword>i64</keyword>
 | 
			
		||||
			<keyword>i128</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>u8</keyword>
 | 
			
		||||
			<keyword>u16</keyword>
 | 
			
		||||
			<keyword>u32</keyword>
 | 
			
		||||
			<keyword>u64</keyword>
 | 
			
		||||
			<keyword>u128</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>f8</keyword>
 | 
			
		||||
			<keyword>f16</keyword>
 | 
			
		||||
			<keyword>f32</keyword>
 | 
			
		||||
			<keyword>f64</keyword>
 | 
			
		||||
			<keyword>f128</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>uf8</keyword>
 | 
			
		||||
			<keyword>uf16</keyword>
 | 
			
		||||
			<keyword>uf32</keyword>
 | 
			
		||||
			<keyword>uf64</keyword>
 | 
			
		||||
			<keyword>uf128</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>int</keyword>
 | 
			
		||||
			<keyword>uint</keyword>
 | 
			
		||||
			<keyword>float</keyword>
 | 
			
		||||
			<keyword>ufloat</keyword>
 | 
			
		||||
			<keyword>bool</keyword>
 | 
			
		||||
			<keyword>byte</keyword>
 | 
			
		||||
			<keyword>char</keyword>
 | 
			
		||||
			<keyword>str</keyword>
 | 
			
		||||
			<keyword>void</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>auto</keyword>
 | 
			
		||||
 | 
			
		||||
			<keyword>dict</keyword>
 | 
			
		||||
			<keyword>list</keyword>
 | 
			
		||||
			<keyword>map</keyword>
 | 
			
		||||
			<keyword>object</keyword>
 | 
			
		||||
			<keyword>range</keyword>
 | 
			
		||||
			<keyword>set</keyword>
 | 
			
		||||
			<keyword>tuple</keyword>
 | 
			
		||||
		</context>
 | 
			
		||||
 | 
			
		||||
		<context id="slang" class="no-spell-check">
 | 
			
		||||
			<include>
 | 
			
		||||
				<context ref="def:shebang"/>
 | 
			
		||||
				<context ref="close-comment-outside-comment" style-ref="def:error"/>
 | 
			
		||||
				<context ref="multiline-comment" style-ref="def:comment"/>
 | 
			
		||||
				<context ref="comment" style-ref="def:comment"/>
 | 
			
		||||
				<context ref="multiline-double-quoted-string"/>
 | 
			
		||||
				<context ref="multiline-single-quoted-string"/>
 | 
			
		||||
				<context ref="double-quoted-string"/>
 | 
			
		||||
				<context ref="single-quoted-string"/>
 | 
			
		||||
				<context ref="char"/>
 | 
			
		||||
				<context ref="boolean"/>
 | 
			
		||||
				<context ref="complex"/>
 | 
			
		||||
				<context ref="float"/>
 | 
			
		||||
				<context ref="def:decimal"/>
 | 
			
		||||
				<context ref="def:octal"/>
 | 
			
		||||
				<context ref="def:hexadecimal"/>
 | 
			
		||||
				<context ref="function-definition"/>
 | 
			
		||||
				<context ref="class-definition"/>
 | 
			
		||||
				<context ref="decorator"/>
 | 
			
		||||
				<context ref="keywords"/>
 | 
			
		||||
				<context ref="reserved-keywords"/>
 | 
			
		||||
				<context ref="builtin-constants"/>
 | 
			
		||||
				<context ref="builtin-objects"/>
 | 
			
		||||
				<context ref="builtin-types"/>
 | 
			
		||||
			</include>
 | 
			
		||||
		</context>
 | 
			
		||||
	</definitions>
 | 
			
		||||
</language>
 | 
			
		||||
							
								
								
									
										9
									
								
								sld.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								sld.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# Slang .sld parser
 | 
			
		||||
# .sld files are used for definition of built-in, implementation-provided, external and native components.
 | 
			
		||||
 | 
			
		||||
from .tokens import *
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
							
								
								
									
										171
									
								
								stdlib.py
									
									
									
									
									
								
							
							
						
						
									
										171
									
								
								stdlib.py
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
#!/usr/bin/python3
 | 
			
		||||
# Slang stdlib
 | 
			
		||||
 | 
			
		||||
from .ast import Signature, Function, Object
 | 
			
		||||
from .ast import Signature, Function, Object, Collection, CallArguments
 | 
			
		||||
from .tokens import *
 | 
			
		||||
from utils import *
 | 
			
		||||
 | 
			
		||||
@@ -9,15 +9,25 @@ class Builtin(Signature):
 | 
			
		||||
	def __init__(self):
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
	@classitemget
 | 
			
		||||
	def attrops(cls, optype, attr):
 | 
			
		||||
		if (optype == '.'):
 | 
			
		||||
			try: return getattr(cls, attr)()
 | 
			
		||||
			except AttributeError: raise KeyError()
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def __reprname__(self):
 | 
			
		||||
		return type(self).mro()[1].__name__
 | 
			
		||||
		return first(i.__name__ for i in self.__class__.mro() if i.__name__.startswith('Builtin'))
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def typename(self):
 | 
			
		||||
		return type(self).__name__
 | 
			
		||||
		return self.__class__.__name__
 | 
			
		||||
 | 
			
		||||
class BuiltinFunction(Builtin, Function): pass
 | 
			
		||||
class BuiltinFunction(Builtin, Function):
 | 
			
		||||
	@property
 | 
			
		||||
	def name(self):
 | 
			
		||||
		return self.__class__.__name__
 | 
			
		||||
 | 
			
		||||
class BuiltinObject(Builtin, Object): pass
 | 
			
		||||
 | 
			
		||||
@@ -27,84 +37,173 @@ class BuiltinType(Builtin):
 | 
			
		||||
	def __init__(self, *, modifiers: paramset):
 | 
			
		||||
		self.modifiers = modifiers
 | 
			
		||||
 | 
			
		||||
class void(BuiltinType): pass
 | 
			
		||||
	def __eq__(self, x):
 | 
			
		||||
		return (super().__eq__(x) or issubclass(x.__class__, self.__class__) or issubclass(self.__class__, x.__class__))
 | 
			
		||||
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def operators(op, valsig=None, selftype=None):
 | 
			
		||||
		if (valsig is None):
 | 
			
		||||
			if (op in operators[9]): return bool  # unary `not'
 | 
			
		||||
		else:
 | 
			
		||||
			assert (selftype is not None)
 | 
			
		||||
			if (isinstance(valsig, selftype) and op in operators[8]): return bool  # comparisons
 | 
			
		||||
			if (op in operators[10]+operators[11]+operators[12]): return type(valsig)  # binary `and'. `xor', `or'
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
class Any(BuiltinType):
 | 
			
		||||
	def __eq__(self, x):
 | 
			
		||||
		return True
 | 
			
		||||
 | 
			
		||||
class void(BuiltinType):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
class bool(BuiltinType):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		try: return BuiltinType.operators(op, valsig=valsig, selftype=bool)
 | 
			
		||||
		except KeyError: pass
 | 
			
		||||
		if (valsig is None):
 | 
			
		||||
			if (op in map(UnaryOperator, '+-~')): return int
 | 
			
		||||
			if (op in map(UnaryOperator, ('not', '!'))): return bool
 | 
			
		||||
			if (op == UnaryOperator('!')): return bool
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
class int(BuiltinType):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		try: return BuiltinType.operators(op, valsig=valsig, selftype=int)
 | 
			
		||||
		except KeyError: pass
 | 
			
		||||
		if (valsig is None):
 | 
			
		||||
			if (op in map(UnaryOperator, '+-~')): return int
 | 
			
		||||
			if (op in map(UnaryOperator, ('not', '!'))): return bool
 | 
			
		||||
		if (not isinstance(valsig, (int, float))): raise KeyError()
 | 
			
		||||
		if (op in map(BinaryOperator, ('**', *'+-*'))): return valsig
 | 
			
		||||
		if (op in map(BinaryOperator, ('//', '<<', '>>', *'&^|'))): return int
 | 
			
		||||
		if (op == BinaryOperator('/')): return float
 | 
			
		||||
		if (op == BinaryOperator('to')): return int
 | 
			
		||||
			if (op == UnaryOperator('!')): return bool
 | 
			
		||||
		if (isinstance(valsig, (int, float))):
 | 
			
		||||
			if (op in map(BinaryOperator, ('**', *'+-*'))): return valsig
 | 
			
		||||
			if (op in map(BinaryOperator, ('//', '<<', '>>', *'&^|'))): return int
 | 
			
		||||
			if (op == BinaryOperator('/')): return float
 | 
			
		||||
		if (isinstance(valsig, int)):
 | 
			
		||||
			if (op == BinaryOperator('to')): return range
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
class float(BuiltinType):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		try: return BuiltinType.operators(op, valsig=valsig, selftype=float)
 | 
			
		||||
		except KeyError: pass
 | 
			
		||||
		if (valsig is None):
 | 
			
		||||
			if (op in map(UnaryOperator, ('not', *'!+-'))): return float
 | 
			
		||||
		if (not isinstance(valsig, (int, float))): raise KeyError()
 | 
			
		||||
		if (op in map(BinaryOperator, ('**', *'+-*'))): return float
 | 
			
		||||
		if (op == BinaryOperator('/')): return float
 | 
			
		||||
		if (op == BinaryOperator('//')): return int
 | 
			
		||||
			if (op in map(UnaryOperator, '+-')): return float
 | 
			
		||||
			if (op == UnaryOperator('!')): return bool
 | 
			
		||||
		if (isinstance(valsig, (int, float))):
 | 
			
		||||
			if (op in map(BinaryOperator, ('**', *'+-*'))): return float
 | 
			
		||||
			if (op == BinaryOperator('/')): return float
 | 
			
		||||
			if (op == BinaryOperator('//')): return int
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
class str(BuiltinType):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		if (valsig is None): raise KeyError()
 | 
			
		||||
		if (isinstance(valsig, str) and op == BinaryOperator('+')): return str
 | 
			
		||||
		if (isinstance(valsig, int) and op == BinaryOperator('*')): return str
 | 
			
		||||
		try: return BuiltinType.operators(op, valsig=valsig, selftype=str)
 | 
			
		||||
		except KeyError: pass
 | 
			
		||||
		if (valsig is not None):
 | 
			
		||||
			if (isinstance(valsig, (char, str)) and op == BinaryOperator('+')): return str
 | 
			
		||||
			if (isinstance(valsig, int) and op == BinaryOperator('*')): return str
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def itemget(keysig):
 | 
			
		||||
	def itemget(keysig, key):
 | 
			
		||||
		if (isinstance(keysig, int)): return char
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
	class rstrip(BuiltinFunction):
 | 
			
		||||
		callargssigstr = "rstrip(char)"
 | 
			
		||||
 | 
			
		||||
		@staticitemget
 | 
			
		||||
		@instantiate
 | 
			
		||||
		def call(callargssig):
 | 
			
		||||
			if (callargssig.kwargs or callargssig.starkwargs): raise KeyError()
 | 
			
		||||
			return str
 | 
			
		||||
 | 
			
		||||
	class count(BuiltinFunction):
 | 
			
		||||
		callargssig = "count(char)"
 | 
			
		||||
 | 
			
		||||
		@staticitemget
 | 
			
		||||
		@instantiate
 | 
			
		||||
		def call(callargssig):
 | 
			
		||||
			if (callargssig.kwargs or callargssig.starkwargs): raise KeyError()
 | 
			
		||||
			return int
 | 
			
		||||
 | 
			
		||||
class char(BuiltinType):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		if (valsig is None): raise KeyError()
 | 
			
		||||
		if (isinstance(valsig, str) and op == BinaryOperator('+')): return str
 | 
			
		||||
		if (isinstance(valsig, int) and op == BinaryOperator('*')): return str
 | 
			
		||||
		if (isinstance(valsig, (char, int)) and op in map(BinaryOperator, '+-')): return char
 | 
			
		||||
		try: return BuiltinType.operators(op, valsig=valsig, selftype=char)
 | 
			
		||||
		except KeyError: pass
 | 
			
		||||
		if (valsig is not None):
 | 
			
		||||
			if (isinstance(valsig, str) and op in map(BinaryOperator, ('+', 'in'))): return str
 | 
			
		||||
			if (isinstance(valsig, int) and op == BinaryOperator('*')): return str
 | 
			
		||||
			if (isinstance(valsig, (char, int)) and op in map(BinaryOperator, '+-')): return char
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
i8 = i16 = i32 = i64 = i128 = \
 | 
			
		||||
u8 = u16 = u32 = u64 = u128 = int
 | 
			
		||||
f8 = f16 = f32 = f64 = f128 = \
 | 
			
		||||
uf8 = uf16 = uf32 = uf64 = uf128 = float
 | 
			
		||||
# TODO: implement these types
 | 
			
		||||
class i8(int): fmt = 'b'
 | 
			
		||||
class u8(int): fmt = 'B'
 | 
			
		||||
class i16(int): fmt = 'h'
 | 
			
		||||
class u16(int): fmt = 'H'
 | 
			
		||||
class i32(int): fmt = 'i'
 | 
			
		||||
class u32(int): fmt = 'I'
 | 
			
		||||
class i64(int): fmt = 'q'
 | 
			
		||||
class u64(int): fmt = 'Q'
 | 
			
		||||
#class i128(int): fmt = '
 | 
			
		||||
#class u128(int): fmt = '
 | 
			
		||||
 | 
			
		||||
class print(BuiltinFunction):
 | 
			
		||||
	callargssigstr = "print(...)"
 | 
			
		||||
#class f8(float): fmt = '
 | 
			
		||||
#class f16(float): fmt = 'e'
 | 
			
		||||
#class f32(float): fmt = 'f'
 | 
			
		||||
#class f64(float): fmt = 'd'
 | 
			
		||||
#class f128(float): fmt = '
 | 
			
		||||
#uf8 = uf16 = uf32 = uf64 = uf128 = float
 | 
			
		||||
 | 
			
		||||
class range(BuiltinType):
 | 
			
		||||
	keytype = int
 | 
			
		||||
	valtype = int
 | 
			
		||||
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	def operators(op, valsig=None):
 | 
			
		||||
		raise KeyError()
 | 
			
		||||
 | 
			
		||||
class iterable(BuiltinType, Collection): pass
 | 
			
		||||
 | 
			
		||||
class list(iterable):
 | 
			
		||||
	keytype = int()
 | 
			
		||||
	valtype = Any()
 | 
			
		||||
 | 
			
		||||
class tuple(iterable):
 | 
			
		||||
	keytype = int()
 | 
			
		||||
	valtype = Any()
 | 
			
		||||
 | 
			
		||||
class stdio(BuiltinObject):
 | 
			
		||||
	class println(BuiltinFunction):
 | 
			
		||||
		callargssigstr = "println(...)"
 | 
			
		||||
 | 
			
		||||
		@staticitemget
 | 
			
		||||
		@instantiate
 | 
			
		||||
		def call(callargssig):
 | 
			
		||||
			if (callargssig.kwargs or callargssig.starkwargs): raise KeyError()
 | 
			
		||||
			return void
 | 
			
		||||
 | 
			
		||||
class _map(BuiltinFunction):
 | 
			
		||||
	@staticitemget
 | 
			
		||||
	@instantiate
 | 
			
		||||
	def call(callargssig):
 | 
			
		||||
		if (callargssig.kwargs or callargssig.starkwargs): raise KeyError()
 | 
			
		||||
		return void
 | 
			
		||||
		return list
 | 
			
		||||
 | 
			
		||||
builtin_names = {j.__name__: globals()[j.__name__] for i in map(operator.methodcaller('__subclasses__'), Builtin.__subclasses__()) for j in i}
 | 
			
		||||
builtin_names.update({i: globals()[i] for i in (i+j for j in map(builtins.str, (8, 16, 32, 64, 128)) for i in (*'iuf', 'uf'))})
 | 
			
		||||
builtin_names = {k: v for i in map(allsubclassdict, Builtin.__subclasses__()) for k, v in i.items()}
 | 
			
		||||
builtin_names.update({i: globals()[i] for i in (i+j for j in map(builtins.str, (8, 16, 32, 64, 128)) for i in (*'iuf', 'uf') if i+j in globals())})
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								stdlib/io.sld
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								stdlib/io.sld
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import sld:std:*;
 | 
			
		||||
 | 
			
		||||
class fd {
 | 
			
		||||
	str read();
 | 
			
		||||
	str read(int n);
 | 
			
		||||
 | 
			
		||||
	int write(str data);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class stdio {
 | 
			
		||||
	str readln();
 | 
			
		||||
 | 
			
		||||
	void print(*args);
 | 
			
		||||
 | 
			
		||||
	void println(*args);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										117
									
								
								stdlib/std.sld
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								stdlib/std.sld
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,117 @@
 | 
			
		||||
class void {}
 | 
			
		||||
 | 
			
		||||
class bool {
 | 
			
		||||
	castable to int;
 | 
			
		||||
 | 
			
		||||
	bool operator !;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class int {
 | 
			
		||||
	castable to bool;
 | 
			
		||||
	castable to float;
 | 
			
		||||
 | 
			
		||||
	int operator +;
 | 
			
		||||
	int operator -;
 | 
			
		||||
	int operator ~;
 | 
			
		||||
 | 
			
		||||
	int operator +int;
 | 
			
		||||
	int operator -int;
 | 
			
		||||
	int operator *int;
 | 
			
		||||
	int operator //int;
 | 
			
		||||
	int operator **int;
 | 
			
		||||
	int operator <<int;
 | 
			
		||||
	int operator >>int;
 | 
			
		||||
	int operator ∫
 | 
			
		||||
	int operator ^int;
 | 
			
		||||
	int operator |int;
 | 
			
		||||
 | 
			
		||||
	range operator to int;
 | 
			
		||||
 | 
			
		||||
	int popcount();
 | 
			
		||||
	int length(int base=2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class float {
 | 
			
		||||
	float operator +;
 | 
			
		||||
	float operator -;
 | 
			
		||||
	bool operator !;
 | 
			
		||||
 | 
			
		||||
	float operator +float;
 | 
			
		||||
	float operator -float;
 | 
			
		||||
	float operator *float;
 | 
			
		||||
	float operator /float;
 | 
			
		||||
	int operator //float;
 | 
			
		||||
	float operator **float;
 | 
			
		||||
 | 
			
		||||
	int round();
 | 
			
		||||
	bool isint();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class char {
 | 
			
		||||
	castable to str;
 | 
			
		||||
 | 
			
		||||
	bool operator !;
 | 
			
		||||
 | 
			
		||||
	char operator +char;
 | 
			
		||||
	char operator +int;
 | 
			
		||||
	char operator -char;
 | 
			
		||||
	char operator -int;
 | 
			
		||||
	char operator *int;
 | 
			
		||||
 | 
			
		||||
	range operator 'to' char;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class str {
 | 
			
		||||
	bool operator !;
 | 
			
		||||
 | 
			
		||||
	iterable char;
 | 
			
		||||
 | 
			
		||||
	char operator [int];
 | 
			
		||||
 | 
			
		||||
	str operator +str;
 | 
			
		||||
	str operator *int;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class range {
 | 
			
		||||
	typename type;
 | 
			
		||||
 | 
			
		||||
	iterable type;
 | 
			
		||||
 | 
			
		||||
	type operator [int];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class list {
 | 
			
		||||
	typename type;
 | 
			
		||||
 | 
			
		||||
	iterable type;
 | 
			
		||||
 | 
			
		||||
	type operator [int];
 | 
			
		||||
 | 
			
		||||
	void append(type item);
 | 
			
		||||
 | 
			
		||||
	void insert(int index, type item);
 | 
			
		||||
 | 
			
		||||
	type pop();
 | 
			
		||||
	type pop(int index);
 | 
			
		||||
 | 
			
		||||
	void reverse();
 | 
			
		||||
 | 
			
		||||
	void sort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class tuple {
 | 
			
		||||
	typename types[];
 | 
			
		||||
 | 
			
		||||
	iterable types;
 | 
			
		||||
 | 
			
		||||
	types operator [int];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class function {
 | 
			
		||||
	typename argtypes[];
 | 
			
		||||
	typename rettype;
 | 
			
		||||
 | 
			
		||||
	call rettype (*argtypes);
 | 
			
		||||
 | 
			
		||||
	rettype map(iterable);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								tests/class.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								tests/class.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,37 @@
 | 
			
		||||
class Test {
 | 
			
		||||
	int a = 1
 | 
			
		||||
 | 
			
		||||
	init {
 | 
			
		||||
		.a = 3
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constr () {
 | 
			
		||||
		.a = 5
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constr (int a) {
 | 
			
		||||
		.a = a
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(Test.a)
 | 
			
		||||
 | 
			
		||||
	Test t
 | 
			
		||||
	stdio.println(t.a)
 | 
			
		||||
	t.a = 2
 | 
			
		||||
	stdio.println(t.a)
 | 
			
		||||
	delete t
 | 
			
		||||
 | 
			
		||||
	Test t = Test()
 | 
			
		||||
	stdio.println(t.a)
 | 
			
		||||
	delete t
 | 
			
		||||
 | 
			
		||||
	Test t = Test(7)
 | 
			
		||||
	stdio.println(t.a)
 | 
			
		||||
	delete t
 | 
			
		||||
 | 
			
		||||
	#Test t(10)
 | 
			
		||||
	#stdio.println(t.a)
 | 
			
		||||
	#delete t
 | 
			
		||||
}
 | 
			
		||||
@@ -18,10 +18,8 @@ auto g(str x) {  # g() is of type char, x is of type str
 | 
			
		||||
 | 
			
		||||
int h(int x) = x+1  # h() is of type int, x is of type int
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
	print(h(n), \
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(h(n), \  # comment here too
 | 
			
		||||
		f('123asd') + g('32') + 1)  #--> «123124 f»
 | 
			
		||||
	print(q/z/2**96)  #--> «4294967296.0»
 | 
			
		||||
	stdio.println(q/z/2**96)  #--> «4294967296.0»
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main()
 | 
			
		||||
							
								
								
									
										8
									
								
								tests/fib.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/fib.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
int fib(int n) {
 | 
			
		||||
	if (n < 2) {return 1}
 | 
			
		||||
	return fib(n-1) + fib(n-2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(fib(3))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										5
									
								
								tests/fmap.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/fmap.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
			
		||||
void f(int x) = stdio.println(x)
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
	f.map([int: 1, 2, 3])
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								tests/for.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/for.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
main {
 | 
			
		||||
	for i in (0 to 5) {
 | 
			
		||||
		stdio.println(i)
 | 
			
		||||
		stdio.println(i+2)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
void f() {}
 | 
			
		||||
void f(int a?) {}
 | 
			
		||||
void f(int a?) {}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								tests/list.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/list.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
list l = [int: 1, 2, 3]
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
	#int i = 0
 | 
			
		||||
	#while (i < 3) {
 | 
			
		||||
	for i in (0 to 3) {
 | 
			
		||||
		stdio.println(i, l[i])
 | 
			
		||||
		#i -=- 1
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								tests/nl.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tests/nl.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(1, #|
 | 
			
		||||
		|# 2)
 | 
			
		||||
}
 | 
			
		||||
@@ -1,2 +1,5 @@
 | 
			
		||||
int a = 3
 | 
			
		||||
print(2**a)
 | 
			
		||||
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(2**a)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
int f(int x) = x+1
 | 
			
		||||
int f(int x, int y) = x+y+1
 | 
			
		||||
 | 
			
		||||
print(f(1))
 | 
			
		||||
print(f(1, 2))
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(f(1))
 | 
			
		||||
	stdio.println(f(1, 2))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										3
									
								
								tests/println.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								tests/println.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println('test')
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								tests/redefine.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								tests/redefine.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
main {
 | 
			
		||||
	int a
 | 
			
		||||
	a = 3
 | 
			
		||||
	a, b := (int a, char 'b')
 | 
			
		||||
	stdio.println(a, b)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								tests/strcat.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tests/strcat.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
str a = "a"
 | 
			
		||||
str b = "B"
 | 
			
		||||
str c = a+b
 | 
			
		||||
stdio.println(c)
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
int a = 3
 | 
			
		||||
int b = 5
 | 
			
		||||
print(a+b)
 | 
			
		||||
i8 a = 3
 | 
			
		||||
i8 b = 5
 | 
			
		||||
i8 c = a+b
 | 
			
		||||
stdio.println(c)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,29 +2,27 @@ const int a = 3
 | 
			
		||||
int b = 2; int c = 0; int x = 9
 | 
			
		||||
 | 
			
		||||
int sum(int a, int z) = a + z
 | 
			
		||||
print(sum(a, b & 3))
 | 
			
		||||
print(1)
 | 
			
		||||
print(1, 2)
 | 
			
		||||
print('a')
 | 
			
		||||
print(2*(3)+-2*(5+1)/2)
 | 
			
		||||
print(-2*x**(2+2)*a*b+10*c)
 | 
			
		||||
print(*'a'+'b'*2)
 | 
			
		||||
print(-2-2-2-2-2-2-2-2)
 | 
			
		||||
stdio.println(sum(a, b & 3))
 | 
			
		||||
stdio.println(1)
 | 
			
		||||
stdio.println(1, 2)
 | 
			
		||||
stdio.println('a')
 | 
			
		||||
stdio.println(2*(3)+-2*(5+1)/2)
 | 
			
		||||
stdio.println(-2*x**(2+2)*a*b+10*c)
 | 
			
		||||
stdio.println(*'a'+'b'*2)
 | 
			
		||||
stdio.println(-2-2-2-2-2-2-2-2)
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
main {
 | 
			
		||||
	int z = sum(3, 2)
 | 
			
		||||
	b = 2**100
 | 
			
		||||
	b *= 2
 | 
			
		||||
	print(not a)
 | 
			
		||||
	print(a, b, c)
 | 
			
		||||
	stdio.println(not a)
 | 
			
		||||
	stdio.println(a, b, c)
 | 
			
		||||
	int test() = 2*2
 | 
			
		||||
	print(sum(b, test()))
 | 
			
		||||
	stdio.println(sum(b, test()))
 | 
			
		||||
 | 
			
		||||
	for i in (0 to 5) print(i)
 | 
			
		||||
	else print(0)
 | 
			
		||||
	for i in 0 to 5 stdio.println(i)
 | 
			
		||||
	else stdio.println(0)
 | 
			
		||||
 | 
			
		||||
	int i = 5
 | 
			
		||||
	while (i) {print(i); i -= 1;} else print('ВСЁ!')
 | 
			
		||||
	while i {stdio.println(i); i -= 1;} else stdio.println('ВСЁ!')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
main()
 | 
			
		||||
							
								
								
									
										4
									
								
								tests/wrongnl.sl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								tests/wrongnl.sl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
main {
 | 
			
		||||
	stdio.println(1, \ 2,
 | 
			
		||||
		3)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										111
									
								
								tokens.py
									
									
									
									
									
								
							
							
						
						
									
										111
									
								
								tokens.py
									
									
									
									
									
								
							@@ -5,12 +5,19 @@ from utils import *
 | 
			
		||||
 | 
			
		||||
class Keyword(str): pass
 | 
			
		||||
class ExprKeyword(Keyword): pass
 | 
			
		||||
class DefKeyword(Keyword): pass
 | 
			
		||||
class DefNamedKeyword(Keyword): pass
 | 
			
		||||
class DefArgsKeyword(Keyword): pass
 | 
			
		||||
class ClassdefKeyword(DefKeyword): pass # TODO validate!
 | 
			
		||||
class ClassdefNamedKeyword(ClassdefKeyword, DefNamedKeyword): pass
 | 
			
		||||
class ClassdefArgsKeyword(ClassdefKeyword, DefArgsKeyword): pass
 | 
			
		||||
class Modifier(Keyword): pass
 | 
			
		||||
class ReservedKeyword(Keyword): pass
 | 
			
		||||
 | 
			
		||||
class Operator(str): pass
 | 
			
		||||
class UnaryOperator(Operator): pass
 | 
			
		||||
class BinaryOperator(Operator): pass
 | 
			
		||||
class BothOperator(UnaryOperator, BinaryOperator): pass
 | 
			
		||||
 | 
			
		||||
keywords = (
 | 
			
		||||
	Keyword('if'),
 | 
			
		||||
@@ -18,17 +25,36 @@ keywords = (
 | 
			
		||||
	Keyword('in'),
 | 
			
		||||
	Keyword('while'),
 | 
			
		||||
	Keyword('else'),
 | 
			
		||||
	Keyword('class'),
 | 
			
		||||
	ExprKeyword('return'),
 | 
			
		||||
	ExprKeyword('break'),
 | 
			
		||||
	ExprKeyword('continue'),
 | 
			
		||||
	ExprKeyword('import'),
 | 
			
		||||
	ExprKeyword('delete'),
 | 
			
		||||
	ExprKeyword('assert'),
 | 
			
		||||
	ExprKeyword('breakpoint'),
 | 
			
		||||
	DefKeyword('main'),
 | 
			
		||||
	DefKeyword('exit'),
 | 
			
		||||
	ClassdefKeyword('init'),
 | 
			
		||||
	ClassdefArgsKeyword('constr'),
 | 
			
		||||
	ClassdefNamedKeyword('property'),
 | 
			
		||||
	ClassdefKeyword('repr'),
 | 
			
		||||
	ClassdefKeyword('eq'),
 | 
			
		||||
	Modifier('const'),
 | 
			
		||||
	Modifier('static'),
 | 
			
		||||
	Modifier('volatile'),
 | 
			
		||||
	ReservedKeyword('def'),
 | 
			
		||||
	ReservedKeyword('try'),
 | 
			
		||||
	ReservedKeyword('catch'),
 | 
			
		||||
	ReservedKeyword('except'),
 | 
			
		||||
	ReservedKeyword('finally'),
 | 
			
		||||
	ReservedKeyword('raise'),
 | 
			
		||||
	ReservedKeyword('with'),
 | 
			
		||||
	ReservedKeyword('yield'),
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
operators = (*(tuple(map(*i)) for i in ( # ordered by priority
 | 
			
		||||
	(UnaryOperator, '!$:~'),
 | 
			
		||||
operators = (*(tuple(map(*i)) for i in (  # ordered by priority
 | 
			
		||||
	(UnaryOperator, '!+-~'),
 | 
			
		||||
	(BinaryOperator, ('**',)),
 | 
			
		||||
	(BinaryOperator, ('//', *'*/%')),
 | 
			
		||||
	(BinaryOperator, '+-'),
 | 
			
		||||
@@ -36,19 +62,19 @@ operators = (*(tuple(map(*i)) for i in ( # ordered by priority
 | 
			
		||||
	(BinaryOperator, '&'),
 | 
			
		||||
	(BinaryOperator, '^'),
 | 
			
		||||
	(BinaryOperator, '|'),
 | 
			
		||||
	(BinaryOperator, ('<', '<=', '>', '>=', '==', '!=', 'is not', 'is')),
 | 
			
		||||
	(BinaryOperator, ('<', '<=', '>', '>=', '==', '!=', 'is', 'is not', 'in', 'not in', 'isof')),
 | 
			
		||||
	(UnaryOperator, ('not',)),
 | 
			
		||||
	(BinaryOperator, ('&&', 'and')),
 | 
			
		||||
	(BinaryOperator, ('&&', 'and', 'but')),
 | 
			
		||||
	(BinaryOperator, ('^^', 'xor')),
 | 
			
		||||
	(BinaryOperator, ('||', 'or')),
 | 
			
		||||
	(BinaryOperator, ('to',)),
 | 
			
		||||
)),)
 | 
			
		||||
bothoperators = '%&*+-^'
 | 
			
		||||
bothoperators = '+-'
 | 
			
		||||
attrops = ('->', '@.', *'@.:')
 | 
			
		||||
keyword_operators = ('is not', 'is', 'not', 'and', 'xor', 'or', 'to')
 | 
			
		||||
logical_operators = ('is', 'is not', 'in', 'not in', 'not', 'and', 'but', 'xor', 'or', 'isof')
 | 
			
		||||
 | 
			
		||||
whitespace = ' \t\r\v\f'
 | 
			
		||||
specials = ('..', '->', '@.', *'#@.\\,;?=()[]{}')
 | 
			
		||||
specials = ('..', ':=', *attrops, *r'#\,;?=()[]{}')
 | 
			
		||||
 | 
			
		||||
def find_identifier(s):
 | 
			
		||||
	if (not s or not s[0].isidentifier()): return
 | 
			
		||||
@@ -63,7 +89,7 @@ def find_keyword(s):
 | 
			
		||||
	for i in keywords:
 | 
			
		||||
		if (s.startswith(i)):
 | 
			
		||||
			l = len(i)
 | 
			
		||||
			if (not s[l:l+1] or s[l:l+1].isspace()): return (l, i)
 | 
			
		||||
			if (not s[l:l+1].isidentifier()): return (l, i)
 | 
			
		||||
 | 
			
		||||
def find_literal(s):
 | 
			
		||||
	if (not s): return
 | 
			
		||||
@@ -78,7 +104,7 @@ def find_literal(s):
 | 
			
		||||
		digits = '0123456789abcdef'
 | 
			
		||||
		radix = 10
 | 
			
		||||
		digit = True
 | 
			
		||||
		dp = s[0] == '.'
 | 
			
		||||
		dp = (s[0] == '.')
 | 
			
		||||
		for i in range(1, len(s)):
 | 
			
		||||
			if (i == 1 and s[0] == '0'):
 | 
			
		||||
				if (s[1] not in 'box'):
 | 
			
		||||
@@ -94,14 +120,14 @@ def find_literal(s):
 | 
			
		||||
				if (not digit or s[i].isalpha()): return
 | 
			
		||||
				return i
 | 
			
		||||
			digit = True
 | 
			
		||||
		if (s[i].casefold() in digits[:radix] or s[i] == '.' and not dp): return i+1
 | 
			
		||||
		if (s[i].casefold() in digits[:radix] or s[i] == '.' and dp): return i+1
 | 
			
		||||
 | 
			
		||||
def find_operator(s):
 | 
			
		||||
	if (not s): return
 | 
			
		||||
	for i in sorted(itertools.chain(*operators), key=len, reverse=True):
 | 
			
		||||
		if (s.startswith(i)):
 | 
			
		||||
			l = len(i)
 | 
			
		||||
			if (not (i[-1].isalpha() and s[l:l+1].isalnum())): return (len(i), i)
 | 
			
		||||
			if (not (i[-1].isalpha() and s[l:l+1].isidentifier())): return (len(i), BothOperator(i) if (i in bothoperators) else i)
 | 
			
		||||
 | 
			
		||||
def find_special(s):
 | 
			
		||||
	if (not s): return
 | 
			
		||||
@@ -114,7 +140,7 @@ def find_special(s):
 | 
			
		||||
		l = s.find('\n', 1)
 | 
			
		||||
		if (l == -1): return len(s)
 | 
			
		||||
		return l
 | 
			
		||||
	if (s[:2] == '\\\n'): return 2
 | 
			
		||||
	if (s[0] == '\\'): return 1
 | 
			
		||||
	for i in sorted(specials, key=len, reverse=True):
 | 
			
		||||
		if (s[:len(i)] == i):
 | 
			
		||||
			if (i == '=' and s[:len(i)+1] == '=='): break
 | 
			
		||||
@@ -127,7 +153,7 @@ def operator_precedence(op):
 | 
			
		||||
 | 
			
		||||
class Token:
 | 
			
		||||
	__slots__ = ('type', 'token', 'lineno', 'offset')
 | 
			
		||||
	types = ('SPECIAL', 'OPERATOR', 'LITERAL', 'KEYWORD', 'IDENTIFIER') # order is also resolution order
 | 
			
		||||
	types = ('SPECIAL', 'OPERATOR', 'LITERAL', 'KEYWORD', 'IDENTIFIER')  # order is also resolution order
 | 
			
		||||
 | 
			
		||||
	def __init__(self, type, token, *, lineno, offset):
 | 
			
		||||
		self.type, self.token, self.lineno, self.offset = type, token, lineno, offset
 | 
			
		||||
@@ -157,11 +183,12 @@ class SlSyntaxNoToken(SlSyntaxException): pass
 | 
			
		||||
class SlSyntaxEmpty(SlSyntaxNoToken): pass
 | 
			
		||||
 | 
			
		||||
class SlSyntaxError(SlSyntaxException):
 | 
			
		||||
	__slots__ = ('desc', 'line', 'lineno', 'offset', 'length')
 | 
			
		||||
	__slots__ = ('desc', 'line', 'lineno', 'offset', 'length', 'usage')
 | 
			
		||||
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	def __init__(self, desc='Syntax error', line='', *, lineno, offset, length, scope=None):
 | 
			
		||||
		self.desc, self.line, self.lineno, self.offset, self.length = (f'\033[2m(in {scope})\033[0m ' if (scope is not None) else '')+desc, line, lineno, offset, length
 | 
			
		||||
		self.usage = None
 | 
			
		||||
 | 
			
		||||
	#@dispatch
 | 
			
		||||
	#def __init__(self, desc='Syntax error', *, token):
 | 
			
		||||
@@ -169,10 +196,12 @@ class SlSyntaxError(SlSyntaxException):
 | 
			
		||||
 | 
			
		||||
	def __str__(self):
 | 
			
		||||
		l, line = lstripcount(self.line.partition('\n')[0].replace('\t', ' '), ' \t')
 | 
			
		||||
		offset = (self.offset-l) if (self.offset != -1) else len(line)
 | 
			
		||||
		offset = (self.offset-l) if (self.offset >= 0) else (len(line)+self.offset+1)
 | 
			
		||||
		#Syntax error: 
 | 
			
		||||
		return f"{self.desc}{self.at}"+(':\n'+\
 | 
			
		||||
			'  \033[1m'+line[:offset]+'\033[91m'+line[offset:]+'\033[0m\n'+\
 | 
			
		||||
			'  '+' '*offset+'\033[95m^'+'~'*(self.length-1) if (line) else '')
 | 
			
		||||
			'  \033[1m'+line[:offset]+'\033[91m'*(self.offset >= 0)+line[offset:]+'\033[0m\n'+\
 | 
			
		||||
			'  '+' '*offset+'\033[95m^'+'~'*(self.length-1)+'\033[0m' if (line) else '') + \
 | 
			
		||||
			(f"\n\n\033[1;95mCaused by:\033[0m\n{self.__cause__}" if (self.__cause__ is not None) else '')
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def at(self):
 | 
			
		||||
@@ -182,25 +211,57 @@ class SlSyntaxExpectedError(SlSyntaxError):
 | 
			
		||||
	__slots__ = ('expected', 'found')
 | 
			
		||||
 | 
			
		||||
	def __init__(self, expected='nothing', found='nothing', *, lineno=None, offset=None, length=0, scope=None):
 | 
			
		||||
		assert expected != found
 | 
			
		||||
		assert (expected != found)
 | 
			
		||||
		if (not isinstance(found, str)): lineno, offset, length, found = found.lineno, found.offset, found.length, found.typename if (hasattr(found, 'typename')) else found
 | 
			
		||||
		assert lineno is not None and offset is not None
 | 
			
		||||
		assert (lineno is not None and offset is not None)
 | 
			
		||||
		super().__init__(f"Expected {expected.lower()},\n{' '*(len(scope)+6 if (scope is not None) else 3)}found {found.lower()}", lineno=lineno, offset=offset, length=length, scope=scope)
 | 
			
		||||
		self.expected, self.found = expected, found
 | 
			
		||||
 | 
			
		||||
class SlSyntaxExpectedNothingError(SlSyntaxExpectedError):
 | 
			
		||||
	def __init__(self, found='nothing', **kwargs):
 | 
			
		||||
	def __init__(self, found, **kwargs):
 | 
			
		||||
		super().__init__(found=found, **kwargs)
 | 
			
		||||
 | 
			
		||||
class SlSyntaxMultiExpectedError(SlSyntaxExpectedError):
 | 
			
		||||
	__slots__ = ('sl',)
 | 
			
		||||
class SlSyntaxExpectedMoreTokensError(SlSyntaxExpectedError):
 | 
			
		||||
	def __init__(self, for_, *, offset=-1, **kwargs):
 | 
			
		||||
		assert (offset < 0)
 | 
			
		||||
		super().__init__(expected=f"More tokens for {for_}", offset=offset, **kwargs)
 | 
			
		||||
 | 
			
		||||
	def __init__(self, expected, found, *, scope=None, **kwargs):
 | 
			
		||||
class SlSyntaxMultiExpectedError(SlSyntaxExpectedError):
 | 
			
		||||
	__slots__ = ('sl', 'errlist')
 | 
			
		||||
 | 
			
		||||
	def __init__(self, expected, found, *, scope=None, errlist=None, **kwargs):
 | 
			
		||||
		self.errlist = errlist
 | 
			
		||||
		self.sl = len(scope)+6 if (scope is not None) else 0
 | 
			
		||||
		super().__init__(S(',\n'+' '*(self.sl+9)).join(Stuple(expected).strip('nothing').uniquize(), last=',\n'+' '*(self.sl+6)+'or ') or 'nothing', S(',\n'+' '*(self.sl+6)).join(Stuple(f"{i.found} at offset {i.offset if (i.offset != -1) else '<end of line>'}" for i in found).strip('nothing').uniquize(), last=',\n'+' '*(self.sl+2)+'and ') or 'nothing', scope=scope, **kwargs)
 | 
			
		||||
		super().__init__(
 | 
			
		||||
			expected=S(',\n'+' '*(self.sl+9)).join(Stuple((i.expected+f" at offset {i.offset if (i.offset >= 0) else '<end of line>'}{i.offset+1 if (i.offset < -1) else ''}" if (not isinstance(i, SlSyntaxMultiExpectedError)) else i.expected)+(f' (for {i.usage})' if (i.usage) else '') for i in expected).strip('nothing').uniquize(str.casefold), last=',\n'+' '*(self.sl+6)+'or ') or 'nothing',
 | 
			
		||||
			found=S(',\n'+' '*(self.sl+6)).join(Stuple(i.found+f" at {f'offset {i.offset}' if (i.offset >= 0) else '<end of line>'}{i.offset+1 if (i.offset < -1) else ''}" if (not isinstance(i, SlSyntaxMultiExpectedError)) else i.expected for i in found).strip('nothing').uniquize(str.casefold), last=',\n'+' '*(self.sl+2)+'and ') or 'nothing',
 | 
			
		||||
			scope=scope,
 | 
			
		||||
			**kwargs
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
	@property
 | 
			
		||||
	def at(self):
 | 
			
		||||
		return f"\n{' '*self.sl}at line {self.lineno}"
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2019
 | 
			
		||||
	@classmethod
 | 
			
		||||
	def from_list(cls, err, scope=None, **kwargs):
 | 
			
		||||
		sl = len(scope)+6 if (scope is not None) else 0
 | 
			
		||||
 | 
			
		||||
		for i in err:
 | 
			
		||||
			if (isinstance(i, SlSyntaxMultiExpectedError)):
 | 
			
		||||
				loff = 0
 | 
			
		||||
				i.expected = ' '*loff+S(i.expected).indent(sl+loff).lstrip()
 | 
			
		||||
				i.found = ' '*loff+S(i.found).indent(sl+loff).lstrip()
 | 
			
		||||
 | 
			
		||||
		return cls(
 | 
			
		||||
			expected=sorted(err, key=operator.attrgetter('expected'), reverse=True),
 | 
			
		||||
			found=sorted(err, key=operator.attrgetter('offset')),
 | 
			
		||||
			lineno=max(err, key=operator.attrgetter('lineno')).lineno,
 | 
			
		||||
			offset=max(err, key=lambda x: x.offset if (x.offset >= 0) else inf).offset,
 | 
			
		||||
			length=min(i.length for i in err if i.length) if (not any(i.offset < 0 for i in err)) else 0,
 | 
			
		||||
			scope=scope,
 | 
			
		||||
			errlist=list(err),
 | 
			
		||||
			**kwargs
 | 
			
		||||
		)
 | 
			
		||||
 | 
			
		||||
# by Sdore, 2020
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user