mirror of
https://github.com/egormanga/Slang.git
synced 2025-03-01 18:09:30 +03:00
Initial commit
This commit is contained in:
commit
7badd31e42
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
old.c
|
||||||
|
**.db
|
||||||
|
**.log
|
||||||
|
**.old
|
||||||
|
**.pyc
|
||||||
|
**__pycache__
|
||||||
|
**_config.py
|
116
SBC/bytecode.md
Normal file
116
SBC/bytecode.md
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
# Slang Bytecode
|
||||||
|
|
||||||
|
|
||||||
|
## Standalone
|
||||||
|
|
||||||
|
### NOP
|
||||||
|
0x00
|
||||||
|
> Does nothing.
|
||||||
|
### RET
|
||||||
|
0x01
|
||||||
|
> Returns `TOS` to caller.
|
||||||
|
|
||||||
|
|
||||||
|
## Unary
|
||||||
|
|
||||||
|
### POS
|
||||||
|
0x10
|
||||||
|
> Pushes `abs(TOS)`.
|
||||||
|
### NEG
|
||||||
|
0x11
|
||||||
|
> Pushes `-TOS`.
|
||||||
|
### NOT
|
||||||
|
0x12
|
||||||
|
> Pushes `!TOS`.
|
||||||
|
### INV
|
||||||
|
0x13
|
||||||
|
> Pushes `~TOS`.
|
||||||
|
### ATOI
|
||||||
|
0x14
|
||||||
|
> Pushes integer of smallest possible width parsed from string `TOS`.
|
||||||
|
### ITOA
|
||||||
|
0x15
|
||||||
|
> Pushes string representation of integer `TOS`.
|
||||||
|
### ITOF
|
||||||
|
0x16
|
||||||
|
> Pushes real of smallest possible width equal to integer `TOS`.
|
||||||
|
### CEIL
|
||||||
|
0x17
|
||||||
|
> Pushes smallest integer of smallest possible width greater or equal to real `TOS`.
|
||||||
|
### FLR
|
||||||
|
0x18
|
||||||
|
> Pushes largest integer of smallest possible width less or equal to real `TOS`.
|
||||||
|
### RND
|
||||||
|
0x19
|
||||||
|
> Pushes integer of smallest possible width equal to rounded `TOS`.
|
||||||
|
### CTOS
|
||||||
|
0x1a
|
||||||
|
> Pushes string consisting of char `TOS` and a null byte.
|
||||||
|
|
||||||
|
|
||||||
|
## Binary
|
||||||
|
|
||||||
|
### ADD
|
||||||
|
0x20
|
||||||
|
> Pushes `TOS1 + TOS`.
|
||||||
|
### SUB
|
||||||
|
0x21
|
||||||
|
> Pushes `TOS1 - TOS`.
|
||||||
|
### MUL
|
||||||
|
0x22
|
||||||
|
> Pushes `TOS1 * TOS`.
|
||||||
|
### DIV
|
||||||
|
0x23
|
||||||
|
> Pushes `TOS1 / TOS`.
|
||||||
|
### FLRDIV
|
||||||
|
0x24
|
||||||
|
> Pushes `TOS1 // TOS`.
|
||||||
|
### MOD
|
||||||
|
0x25
|
||||||
|
> Pushes `TOS1 % TOS`.
|
||||||
|
### POW
|
||||||
|
0x26
|
||||||
|
> Pushes `TOS1 ** TOS`.
|
||||||
|
### SHL
|
||||||
|
0x27
|
||||||
|
> Pushes `TOS1 << TOS`.
|
||||||
|
### SHR
|
||||||
|
0x28
|
||||||
|
> Pushes `TOS1 >> TOS`.
|
||||||
|
### AND
|
||||||
|
0x29
|
||||||
|
> Pushes `TOS1 & TOS`.
|
||||||
|
### OR
|
||||||
|
0x2a
|
||||||
|
> Pushes `TOS1 | TOS`.
|
||||||
|
### XOR
|
||||||
|
0x2b
|
||||||
|
> Pushes `TOS1 ^ TOS`.
|
||||||
|
|
||||||
|
|
||||||
|
## With argument
|
||||||
|
|
||||||
|
### ALLOC*(bytes)*
|
||||||
|
0xa0
|
||||||
|
> Pushes reference to `calloc(1, bytes)`.
|
||||||
|
### EXTEND*(bytes)*
|
||||||
|
0xa1
|
||||||
|
> Extends integer `TOS` width to `bytes` bytes if narrower.
|
||||||
|
### CONST*(bytes)*
|
||||||
|
0xa2
|
||||||
|
> Reads next `bytes` bytes of bytecode and pushes a reference to a copy of them.
|
||||||
|
### JUMPF*(offset)*
|
||||||
|
0xa3
|
||||||
|
> Jumps `offset` bytes of bytecode forward.
|
||||||
|
### JUMPB*(offset)*
|
||||||
|
0xa4
|
||||||
|
> Jumps `offset` bytes of bytecode backward.
|
||||||
|
### SCPGET*(cell)*
|
||||||
|
0xa5
|
||||||
|
> Pushes the value of cell `cell` of local scope variables array.
|
||||||
|
### SCPSET*(cell)*
|
||||||
|
0xa6
|
||||||
|
> Sets the value of cell `cell` of local scope variables array to `TOS`.
|
||||||
|
### CALL*(nargs)*
|
||||||
|
0xa7
|
||||||
|
> Pops `nargs ^ (1 << 7)` TODO
|
BIN
SBC/tests/hw.sbc
Normal file
BIN
SBC/tests/hw.sbc
Normal file
Binary file not shown.
170
Slang.md
Normal file
170
Slang.md
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
# Slang
|
||||||
|
<!-- <font size=0>(June, 30 12:04 AM draft)</font> -->
|
||||||
|
|
||||||
|
### Example code
|
||||||
|
<!-- TODO: Slang syntax highlighting -->
|
||||||
|
```c
|
||||||
|
# this is a comment
|
||||||
|
#| and that is a
|
||||||
|
multiline one. |#
|
||||||
|
|
||||||
|
const u32 n = 123123 # n is of type const u32
|
||||||
|
const i64 m = 10**18 # m is of type const i64
|
||||||
|
const int z = 2**128 # z is of type const int (unsized)
|
||||||
|
const auto q = 2**256 # q is of type const int
|
||||||
|
|
||||||
|
char f(str x) { # f() is of type char, x is of type str
|
||||||
|
auto c = x[1] # c is of type char
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
char 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), \
|
||||||
|
f('123asd') + g('32') + 1) #--> «123124 f»
|
||||||
|
print(q/z/2**96) #--> «4294967296.0»
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tokens
|
||||||
|
|
||||||
|
* [Keywords](#Keywords)
|
||||||
|
* [Identifiers](#Identifiers)
|
||||||
|
* [Literals](#Literals)
|
||||||
|
* [Operators](#Operators)
|
||||||
|
* [Specials](#Specials)
|
||||||
|
|
||||||
|
#### Token resolution order
|
||||||
|
|
||||||
|
1. Special
|
||||||
|
2. Operator
|
||||||
|
3. Literal
|
||||||
|
4. Keyword
|
||||||
|
5. Identifier
|
||||||
|
|
||||||
|
## Syntax structures
|
||||||
|
|
||||||
|
_Note: `*` after syntax unit means any number of them._
|
||||||
|
|
||||||
|
### Abstract
|
||||||
|
|
||||||
|
* `{[<\n | ;>]* <<expr> <\n | ;>>* [<\n | ;>]*}` — `code`
|
||||||
|
* `<<expr> | <code>>` — `block`
|
||||||
|
|
||||||
|
### Primitive
|
||||||
|
|
||||||
|
* `<<literal> | <funccall> | <attrget> | <itemget> | <identifier> | <lambda>>` — `value`
|
||||||
|
* `<value>\[<value>\]` — `itemget`
|
||||||
|
* `<(<expr>) | <value> | <operator> <expr> | <expr> <operator> <expr>>` — `expr` (`f(x+3)` is an instance of `expr`, also `f`, `x+3` and `x` are `expr`s too)
|
||||||
|
|
||||||
|
### Non-final
|
||||||
|
|
||||||
|
* `[modifier]* <type>` — `typedef` (left-hand type definition)
|
||||||
|
* `[typedef] <identifier> [? | + | * | ** | =<expr>]` — `argdef` (argument definition)
|
||||||
|
> `?` — if present then argument value, `none` else.<br>
|
||||||
|
> `+` — tuple with at least one argument.<br>
|
||||||
|
> `*` — tuple with any number of arguments.<br>
|
||||||
|
> `**` — object with keyword arguments.<br>
|
||||||
|
> `=` — default value if argument not specified.
|
||||||
|
* `([<argdef>[, <argdef>]*]) -> <typedef> = <expr>` — `lambda` (lambda function)
|
||||||
|
* `<<expr>[, <expr>]*[, *<expr>] | *<expr>>` — `callargs`
|
||||||
|
* `<<identifier>=<expr>[, <identifier>=<expr>]*[, **<expr>] | **<expr>>` — `callkwargs`
|
||||||
|
|
||||||
|
### Final (ordered by resolution order)
|
||||||
|
|
||||||
|
* `<typedef> <identifier>([<argdef>[, <argdef>]*]) <<code> | = <expr>>` — `funcdef` (function definition)
|
||||||
|
* `<exprkeyword> [expr]` — `keywordexpr` (keyword expression)
|
||||||
|
* `<typedef> <identifier> [= <value>]` — `vardef` (variable definition)
|
||||||
|
* `<identifier> = <value>` — `assignment`
|
||||||
|
* `<value>([<callargs> | <callkwargs> | <callargs>, <callkwargs>])` — `funccall` (function call)
|
||||||
|
* `<expr>` — expr evaluation (only in REPL)
|
||||||
|
* `if (<expr>) <block>` — `conditional`
|
||||||
|
* `for (<identifier> in <expr>) <block>` — `forloop`
|
||||||
|
* `while (<expr>) <block>` — `whileloop`
|
||||||
|
* `else <block>` — `elseclause`
|
||||||
|
|
||||||
|
## Keywords
|
||||||
|
|
||||||
|
* `return [expr]` — return from function
|
||||||
|
|
||||||
|
### Modifiers
|
||||||
|
|
||||||
|
* `const` — immutable/constant variable
|
||||||
|
|
||||||
|
### Reserved keywords
|
||||||
|
|
||||||
|
* `def`
|
||||||
|
|
||||||
|
## Identifiers
|
||||||
|
|
||||||
|
Non-empty sequence of alphanumeric characters plus underscore («_»), not starting with a digit character.
|
||||||
|
|
||||||
|
Regex: `[_\w]+[_\w\d]*`
|
||||||
|
|
||||||
|
### Data types
|
||||||
|
|
||||||
|
* `i8`, `i16`, `i32`, `i64`, `i128` — fixed size integer
|
||||||
|
* `u8`, `u16`, `u32`, `u64`, `u128` — fixed size unsigned integer
|
||||||
|
* `f8`, `f16`, `f32`, `f64`, `f128` — fixed size IEEE-754 floating point number
|
||||||
|
* `uf8`, `uf16`, `uf32`, `uf64`, `uf128` — fixed size unsigned floating point number
|
||||||
|
* `int` — unsized («big») integer
|
||||||
|
* `uint` — unsized unsigned integer
|
||||||
|
* `float` — unsized floating point number
|
||||||
|
* `ufloat` — unsized unsigned floating point
|
||||||
|
* `bool` — logical (boolean) value
|
||||||
|
* `byte` — single byte
|
||||||
|
* `char` — UTF-8 character
|
||||||
|
* `str` — UTF-8 string
|
||||||
|
* `void` — nothing (or anything...)
|
||||||
|
|
||||||
|
* `auto` — compile-time type deduction based on value
|
||||||
|
|
||||||
|
## Literals
|
||||||
|
|
||||||
|
_Note: `*` after syntax unit here means any number of them, `+` means at least one._
|
||||||
|
|
||||||
|
* `<<0<b | o | x>><digit+> | <digit+>.<digit*> | <digit*>.<digit+>>` — number
|
||||||
|
* `<"<character*>" | '<character*>'>` — string
|
||||||
|
|
||||||
|
## Operators
|
||||||
|
|
||||||
|
* `<operator> <operand>` — unary operators usage
|
||||||
|
* `<operand> <operator> <operand>` — binary operators usage
|
||||||
|
* `<operand> <operator>= <operand>` — in-place operator usage
|
||||||
|
|
||||||
|
### Character operators
|
||||||
|
|
||||||
|
A set of pre-defined character operators:
|
||||||
|
|
||||||
|
* `!$%&*+-:^~` — unary charset
|
||||||
|
* `%&*+-/<=>^|` — binary charset
|
||||||
|
* `==`, `**`, `//`, `<<`, `>>` — double-char binary operators
|
||||||
|
|
||||||
|
### Keyword operators
|
||||||
|
|
||||||
|
A set of pre-defined keyword operators:
|
||||||
|
|
||||||
|
* `not` — unary keyword operator
|
||||||
|
* `and`, `or`, `xor`, `is`, `is not`, `to` — binary keyword operators
|
||||||
|
|
||||||
|
## Specials
|
||||||
|
|
||||||
|
* `#`, `#|`, `|#` — comment specials
|
||||||
|
* `;` — expr separator special
|
||||||
|
* `->`, `@.`, `@`, `.`, `:` — attrget optype specials
|
||||||
|
* `[`, `]` — itemget specials
|
||||||
|
* `\,?=(){}` — other specials charset
|
||||||
|
|
||||||
|
# Footnotes
|
||||||
|
|
||||||
|
All character class checks are performed in current locale.
|
||||||
|
|
||||||
|
---
|
||||||
|
_by Sdore, 2019_
|
60
Slang.py
Executable file
60
Slang.py
Executable file
@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# 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>'):
|
||||||
|
try:
|
||||||
|
#print(f"Source: {{\n{S(src).indent()}\n}}\n")
|
||||||
|
|
||||||
|
tl = parse_string(src)
|
||||||
|
#print(f"Tokens:\n{pformat(tl)}\n")
|
||||||
|
|
||||||
|
ast = build_ast(tl, filename)
|
||||||
|
#print(f"Code: {repr(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")
|
||||||
|
|
||||||
|
code = PyssemblyCompiler.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]
|
||||||
|
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
|
||||||
|
|
||||||
|
def main(cargs):
|
||||||
|
if (cargs.o is None and not cargs.file.name.rpartition('.')[0]):
|
||||||
|
argparser.add_argument('-o', metavar='<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))
|
||||||
|
|
||||||
|
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))
|
||||||
|
else: logimported()
|
||||||
|
|
||||||
|
# by Sdore, 2019
|
1
__main__.py
Symbolic link
1
__main__.py
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
Slang.py
|
38
compilers/__init__.py
Normal file
38
compilers/__init__.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Slang compilers
|
||||||
|
|
||||||
|
import abc
|
||||||
|
|
||||||
|
class Compiler(abc.ABC):
|
||||||
|
@abc.abstractclassmethod
|
||||||
|
def compile_ast(cls, ast):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def lstripcount(s, chars): # TODO: commonize
|
||||||
|
for ii, i in enumerate(s):
|
||||||
|
if (i not in chars): break
|
||||||
|
else: ii = 0
|
||||||
|
return (ii, s[ii:])
|
||||||
|
|
||||||
|
class SlCompilationError(Exception):
|
||||||
|
__slots__ = ('desc', 'node', 'line', 'scope')
|
||||||
|
|
||||||
|
def __init__(self, desc, node, line='', *, scope=None):
|
||||||
|
self.desc, self.node, self.line, self.scope = desc, node, line, scope
|
||||||
|
|
||||||
|
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 '')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def at(self):
|
||||||
|
return f" at line {self.node.lineno}, offset {self.node.offset}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lineno(self):
|
||||||
|
return self.node.lineno
|
||||||
|
|
||||||
|
# by Sdore, 2019
|
278
compilers/pyssembly.py
Normal file
278
compilers/pyssembly.py
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
#!/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
|
288
compilers/sbc.py
Normal file
288
compilers/sbc.py
Normal file
@ -0,0 +1,288 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Slang Bytecode (SBC) compiler target
|
||||||
|
|
||||||
|
from . import *
|
||||||
|
from Slang.ast import *
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
NOP = 0x00
|
||||||
|
RET = 0x01
|
||||||
|
|
||||||
|
POS = 0x10
|
||||||
|
NEG = 0x11
|
||||||
|
NOT = 0x12
|
||||||
|
INV = 0x13
|
||||||
|
ATOI = 0x14
|
||||||
|
ITOA = 0x15
|
||||||
|
ITOF = 0x16
|
||||||
|
CEIL = 0x17
|
||||||
|
FLR = 0x18
|
||||||
|
RND = 0x19
|
||||||
|
CTOS = 0x1a
|
||||||
|
|
||||||
|
ADD = 0x20
|
||||||
|
SUB = 0x21
|
||||||
|
MUL = 0x22
|
||||||
|
DIV = 0x23
|
||||||
|
FLRDIV = 0x24
|
||||||
|
MOD = 0x25
|
||||||
|
POW = 0x26
|
||||||
|
SHL = 0x27
|
||||||
|
SHR = 0x28
|
||||||
|
AND = 0x29
|
||||||
|
OR = 0x2a
|
||||||
|
XOR = 0x2b
|
||||||
|
|
||||||
|
ALLOC = 0xa0
|
||||||
|
EXTEND = 0xa1
|
||||||
|
CONST = 0xa2
|
||||||
|
JUMPF = 0xa3
|
||||||
|
JUMPB = 0xa4
|
||||||
|
SCPGET = 0xa5
|
||||||
|
SCPSET = 0xa6
|
||||||
|
|
||||||
|
HASARG = 0xa0
|
||||||
|
|
||||||
|
class Instrs:
|
||||||
|
unops = '+-!~'
|
||||||
|
binops = (*'+-*/%', '**', '<<', '>>', '&', '|', '^')
|
||||||
|
|
||||||
|
@init_defaults
|
||||||
|
def __init__(self, *, name, ns, filename, argdefs=()):
|
||||||
|
self.ns = ns
|
||||||
|
self.instrs = bytearray()
|
||||||
|
|
||||||
|
def compile(self):
|
||||||
|
raise TODO
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def add(self, opcode: int, oparg: int = None):
|
||||||
|
self.instrs.append(opcode)
|
||||||
|
if (opcode >= HASARG): self.instrs.append(oparg)
|
||||||
|
else: assert oparg is None
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def add(self, x: ASTRootNode):
|
||||||
|
self.add(x.code)
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def add(self, x: ASTCodeNode):
|
||||||
|
lastln = int()
|
||||||
|
for i in x.nodes:
|
||||||
|
self.add(i)
|
||||||
|
|
||||||
|
@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
|
42
lexer.py
Normal file
42
lexer.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Slang lexer
|
||||||
|
|
||||||
|
from .tokens import *
|
||||||
|
|
||||||
|
def read_token(src, *, lineno, offset, lineoff):
|
||||||
|
(l, src), line = lstripcount(src[offset:], whitespace), src
|
||||||
|
offset += l
|
||||||
|
if (src[:1] in '\n;'): return (offset, None)
|
||||||
|
length = int()
|
||||||
|
for ii, i in enumerate(Token.types):
|
||||||
|
r = globals()['find_'+i.casefold()](src) or 0
|
||||||
|
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)
|
||||||
|
|
||||||
|
def parse_expr(src, *, lineno=1, lineoff=0):
|
||||||
|
r = list()
|
||||||
|
offset = int()
|
||||||
|
while (True):
|
||||||
|
offset, tok = read_token(src, lineno=lineno, offset=offset, lineoff=lineoff)
|
||||||
|
if (tok is None): break
|
||||||
|
r.append(tok)
|
||||||
|
return offset, r
|
||||||
|
|
||||||
|
def parse_string(src):
|
||||||
|
src = src.rstrip()
|
||||||
|
tl = list()
|
||||||
|
lines = src.count('\n')
|
||||||
|
lineoff = int()
|
||||||
|
while (src):
|
||||||
|
offset, r = parse_expr(src, lineno=lines-src.count('\n')+1, lineoff=lineoff)
|
||||||
|
lineoff += offset
|
||||||
|
if (offset < len(src)):
|
||||||
|
if (src[offset] == '\n'): lineoff = int()
|
||||||
|
else: lineoff += 1
|
||||||
|
src = src[offset+1:]
|
||||||
|
tl.append(r)
|
||||||
|
return tl
|
||||||
|
|
||||||
|
# by Sdore, 2019
|
89
repl.py
Normal file
89
repl.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Slang REPL
|
||||||
|
|
||||||
|
import readline
|
||||||
|
from .ast import *
|
||||||
|
from .lexer import *
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTCodeNode, ns):
|
||||||
|
for i in node.nodes:
|
||||||
|
r = execute_node(i, ns)
|
||||||
|
if (r is not None):
|
||||||
|
ns.values['_'] = r
|
||||||
|
print(repr(r))
|
||||||
|
else: return
|
||||||
|
return r
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTVardefNode, ns):
|
||||||
|
ns.values[node.name] = node.value
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTFunccallNode, ns):
|
||||||
|
code_ns = ns.derive(node.callable.value.identifier)
|
||||||
|
argdefs, func = ns.values[node.callable]
|
||||||
|
for ii, i in enumerate(node.callargs.callargs):
|
||||||
|
code_ns.values[argdefs[ii].name] = i
|
||||||
|
return execute_node(func, code_ns)
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTValueNode, ns):
|
||||||
|
return execute_node(node.value, ns)
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTIdentifierNode, ns):
|
||||||
|
return ns.values[node]
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTLiteralNode, ns):
|
||||||
|
return eval(str(node.literal))
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTKeywordExprNode, ns):
|
||||||
|
if (node.keyword.keyword == 'return'): return execute_node(node.value, ns)
|
||||||
|
raise NotImplementedError(node.keyword)
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTUnaryExprNode, ns):
|
||||||
|
return eval(f"{node.operator.operator} {execute_node(node.value, ns)}")
|
||||||
|
|
||||||
|
@dispatch
|
||||||
|
def execute_node(node: ASTBinaryExprNode, ns):
|
||||||
|
return eval(f"{execute_node(node.lvalue, ns)} {node.operator.operator} {execute_node(node.rvalue, ns)}")
|
||||||
|
|
||||||
|
def repl():
|
||||||
|
ns = Namespace('<repl>')
|
||||||
|
#ns.values['print'] = print
|
||||||
|
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
|
110
stdlib.py
Normal file
110
stdlib.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Slang stdlib
|
||||||
|
|
||||||
|
from .ast import Signature, Function, Object
|
||||||
|
from .tokens import *
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
class Builtin(Signature):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def __reprname__(self):
|
||||||
|
return type(self).mro()[1].__name__
|
||||||
|
|
||||||
|
@property
|
||||||
|
def typename(self):
|
||||||
|
return type(self).__name__
|
||||||
|
|
||||||
|
class BuiltinFunction(Builtin, Function): pass
|
||||||
|
|
||||||
|
class BuiltinObject(Builtin, Object): pass
|
||||||
|
|
||||||
|
class BuiltinType(Builtin):
|
||||||
|
@init_defaults
|
||||||
|
@autocast
|
||||||
|
def __init__(self, *, modifiers: paramset):
|
||||||
|
self.modifiers = modifiers
|
||||||
|
|
||||||
|
class void(BuiltinType): pass
|
||||||
|
|
||||||
|
class bool(BuiltinType):
|
||||||
|
@staticitemget
|
||||||
|
@instantiate
|
||||||
|
def operators(op, valsig=None):
|
||||||
|
if (valsig is None):
|
||||||
|
if (op in map(UnaryOperator, '+-~')): return int
|
||||||
|
if (op in map(UnaryOperator, ('not', '!'))): return bool
|
||||||
|
raise KeyError()
|
||||||
|
|
||||||
|
class int(BuiltinType):
|
||||||
|
@staticitemget
|
||||||
|
@instantiate
|
||||||
|
def operators(op, valsig=None):
|
||||||
|
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
|
||||||
|
raise KeyError()
|
||||||
|
|
||||||
|
class float(BuiltinType):
|
||||||
|
@staticitemget
|
||||||
|
@instantiate
|
||||||
|
def operators(op, valsig=None):
|
||||||
|
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
|
||||||
|
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
|
||||||
|
raise KeyError()
|
||||||
|
|
||||||
|
@staticitemget
|
||||||
|
@instantiate
|
||||||
|
def itemget(keysig):
|
||||||
|
if (isinstance(keysig, int)): return char
|
||||||
|
raise KeyError()
|
||||||
|
|
||||||
|
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
|
||||||
|
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 print(BuiltinFunction):
|
||||||
|
callargssigstr = "print(...)"
|
||||||
|
|
||||||
|
@staticitemget
|
||||||
|
@instantiate
|
||||||
|
def call(callargssig):
|
||||||
|
if (callargssig.kwargs or callargssig.starkwargs): raise KeyError()
|
||||||
|
return void
|
||||||
|
|
||||||
|
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'))})
|
||||||
|
|
||||||
|
# by Sdore, 2019
|
27
tests/example.sl
Normal file
27
tests/example.sl
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# this is a comment
|
||||||
|
#| and that is a
|
||||||
|
multiline one. |#
|
||||||
|
|
||||||
|
const u32 n = 123123 # n is of type const u32
|
||||||
|
const i64 m = 10**18 # m is of type const i64
|
||||||
|
const int z = 2**128 # z is of type const int (unsized)
|
||||||
|
const auto q = 2**256 # q is of type const int
|
||||||
|
|
||||||
|
char f(str x) { # f() is of type char, x is of type str
|
||||||
|
auto c = x[1] # c is of type char
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
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), \
|
||||||
|
f('123asd') + g('32') + 1) #--> «123124 f»
|
||||||
|
print(q/z/2**96) #--> «4294967296.0»
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
2
tests/funcdef.sl
Normal file
2
tests/funcdef.sl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
void f() {}
|
||||||
|
void f(int a?) {}
|
2
tests/opti.sl
Normal file
2
tests/opti.sl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
int a = 3
|
||||||
|
print(2**a)
|
5
tests/overload.sl
Normal file
5
tests/overload.sl
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
int f(int x) = x+1
|
||||||
|
int f(int x, int y) = x+y+1
|
||||||
|
|
||||||
|
print(f(1))
|
||||||
|
print(f(1, 2))
|
3
tests/sum.sl
Normal file
3
tests/sum.sl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
int a = 3
|
||||||
|
int b = 5
|
||||||
|
print(a+b)
|
30
tests/test.sl
Normal file
30
tests/test.sl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
int z = sum(3, 2)
|
||||||
|
b = 2**100
|
||||||
|
b *= 2
|
||||||
|
print(not a)
|
||||||
|
print(a, b, c)
|
||||||
|
int test() = 2*2
|
||||||
|
print(sum(b, test()))
|
||||||
|
|
||||||
|
for i in (0 to 5) print(i)
|
||||||
|
else print(0)
|
||||||
|
|
||||||
|
int i = 5
|
||||||
|
while (i) {print(i); i -= 1;} else print('ВСЁ!')
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
1
tests/underscore.sl
Normal file
1
tests/underscore.sl
Normal file
@ -0,0 +1 @@
|
|||||||
|
int _a_b_
|
206
tokens.py
Normal file
206
tokens.py
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# Slang Tokens
|
||||||
|
|
||||||
|
from utils import *
|
||||||
|
|
||||||
|
class Keyword(str): pass
|
||||||
|
class ExprKeyword(Keyword): pass
|
||||||
|
class Modifier(Keyword): pass
|
||||||
|
class ReservedKeyword(Keyword): pass
|
||||||
|
|
||||||
|
class Operator(str): pass
|
||||||
|
class UnaryOperator(Operator): pass
|
||||||
|
class BinaryOperator(Operator): pass
|
||||||
|
|
||||||
|
keywords = (
|
||||||
|
Keyword('if'),
|
||||||
|
Keyword('for'),
|
||||||
|
Keyword('in'),
|
||||||
|
Keyword('while'),
|
||||||
|
Keyword('else'),
|
||||||
|
ExprKeyword('return'),
|
||||||
|
ExprKeyword('break'),
|
||||||
|
ExprKeyword('continue'),
|
||||||
|
ExprKeyword('import'),
|
||||||
|
Modifier('const'),
|
||||||
|
Modifier('volatile'),
|
||||||
|
ReservedKeyword('def'),
|
||||||
|
)
|
||||||
|
|
||||||
|
operators = (*(tuple(map(*i)) for i in ( # ordered by priority
|
||||||
|
(UnaryOperator, '!$:~'),
|
||||||
|
(BinaryOperator, ('**',)),
|
||||||
|
(BinaryOperator, ('//', *'*/%')),
|
||||||
|
(BinaryOperator, '+-'),
|
||||||
|
(BinaryOperator, ('<<', '>>')),
|
||||||
|
(BinaryOperator, '&'),
|
||||||
|
(BinaryOperator, '^'),
|
||||||
|
(BinaryOperator, '|'),
|
||||||
|
(BinaryOperator, ('<', '<=', '>', '>=', '==', '!=', 'is not', 'is')),
|
||||||
|
(UnaryOperator, ('not',)),
|
||||||
|
(BinaryOperator, ('&&', 'and')),
|
||||||
|
(BinaryOperator, ('^^', 'xor')),
|
||||||
|
(BinaryOperator, ('||', 'or')),
|
||||||
|
(BinaryOperator, ('to',)),
|
||||||
|
)),)
|
||||||
|
bothoperators = '%&*+-^'
|
||||||
|
attrops = ('->', '@.', *'@.:')
|
||||||
|
keyword_operators = ('is not', 'is', 'not', 'and', 'xor', 'or', 'to')
|
||||||
|
|
||||||
|
whitespace = ' \t\r\v\f'
|
||||||
|
specials = ('..', '->', '@.', *'#@.\\,;?=()[]{}')
|
||||||
|
|
||||||
|
def find_identifier(s):
|
||||||
|
if (not s or not s[0].isidentifier()): return
|
||||||
|
i = 1
|
||||||
|
for i in range(1, len(s)):
|
||||||
|
if (not s[i].isalnum() and s[i] != '_'): break
|
||||||
|
else: i += 1
|
||||||
|
if (s[:i].isidentifier()): return i
|
||||||
|
|
||||||
|
def find_keyword(s):
|
||||||
|
if (not s): return
|
||||||
|
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)
|
||||||
|
|
||||||
|
def find_literal(s):
|
||||||
|
if (not s): return
|
||||||
|
if (s[0] in '"\''):
|
||||||
|
esc = bool()
|
||||||
|
for i in range(1, len(s)):
|
||||||
|
if (esc): esc = False; continue
|
||||||
|
if (s[i] == '\\'): esc = True; continue
|
||||||
|
if (s[i] == s[0]): return i+1
|
||||||
|
if (s[0].isdigit() or s[0] == '.'):
|
||||||
|
i = int()
|
||||||
|
digits = '0123456789abcdef'
|
||||||
|
radix = 10
|
||||||
|
digit = True
|
||||||
|
dp = s[0] == '.'
|
||||||
|
for i in range(1, len(s)):
|
||||||
|
if (i == 1 and s[0] == '0'):
|
||||||
|
if (s[1] not in 'box'):
|
||||||
|
if (s[1].isalnum()): return
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
radix = (2, 8, 16)['box'.index(s[1])]
|
||||||
|
digit = False
|
||||||
|
continue
|
||||||
|
if (s[i].casefold() not in digits[:radix]):
|
||||||
|
if (s[i] == '_'): continue
|
||||||
|
if (s[i] == '.' and not dp): dp = True; continue
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def find_special(s):
|
||||||
|
if (not s): return
|
||||||
|
if (s[0] == '.' and s[1:2].isdigit()): return
|
||||||
|
if (s[:2] == '#|'):
|
||||||
|
l = s.find('|#', 2)
|
||||||
|
if (l == -1): return -2
|
||||||
|
return l+2
|
||||||
|
if (s[0] == '#'):
|
||||||
|
l = s.find('\n', 1)
|
||||||
|
if (l == -1): return len(s)
|
||||||
|
return l
|
||||||
|
if (s[:2] == '\\\n'): return 2
|
||||||
|
for i in sorted(specials, key=len, reverse=True):
|
||||||
|
if (s[:len(i)] == i):
|
||||||
|
if (i == '=' and s[:len(i)+1] == '=='): break
|
||||||
|
return len(i)
|
||||||
|
|
||||||
|
def operator_precedence(op):
|
||||||
|
for ii, i in enumerate(operators):
|
||||||
|
if (op in i): return ii
|
||||||
|
#else: return len(operators)
|
||||||
|
|
||||||
|
class Token:
|
||||||
|
__slots__ = ('type', 'token', 'lineno', 'offset')
|
||||||
|
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
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"<Token {self.types[self.type]} «{repr(self.token)[1:-1]}» at line {self.lineno}, offset {self.offset}>"
|
||||||
|
|
||||||
|
def __eq__(self, x):
|
||||||
|
return super() == x or self.token == x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def typename(self):
|
||||||
|
return self.types[self.type]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def length(self):
|
||||||
|
return len(self.token)
|
||||||
|
|
||||||
|
def lstripcount(s, chars):
|
||||||
|
for ii, i in enumerate(s):
|
||||||
|
if (i not in chars): break
|
||||||
|
else: ii = 0
|
||||||
|
return (ii, s[ii:])
|
||||||
|
|
||||||
|
class SlSyntaxException(Exception): pass
|
||||||
|
class SlSyntaxNoToken(SlSyntaxException): pass
|
||||||
|
class SlSyntaxEmpty(SlSyntaxNoToken): pass
|
||||||
|
|
||||||
|
class SlSyntaxError(SlSyntaxException):
|
||||||
|
__slots__ = ('desc', 'line', 'lineno', 'offset', 'length')
|
||||||
|
|
||||||
|
#@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
|
||||||
|
|
||||||
|
#@dispatch
|
||||||
|
#def __init__(self, desc='Syntax error', *, token):
|
||||||
|
# self.desc, self.line, self.lineno, self.offset, self.length = desc, '', token.lineno, token.offset, token.length
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 '')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def at(self):
|
||||||
|
return f" at line {self.lineno}, offset {self.offset}"
|
||||||
|
|
||||||
|
class SlSyntaxExpectedError(SlSyntaxError):
|
||||||
|
__slots__ = ('expected', 'found')
|
||||||
|
|
||||||
|
def __init__(self, expected='nothing', found='nothing', *, lineno=None, offset=None, length=0, scope=None):
|
||||||
|
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
|
||||||
|
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):
|
||||||
|
super().__init__(found=found, **kwargs)
|
||||||
|
|
||||||
|
class SlSyntaxMultiExpectedError(SlSyntaxExpectedError):
|
||||||
|
__slots__ = ('sl',)
|
||||||
|
|
||||||
|
def __init__(self, expected, found, *, scope=None, **kwargs):
|
||||||
|
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)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def at(self):
|
||||||
|
return f"\n{' '*self.sl}at line {self.lineno}"
|
||||||
|
|
||||||
|
# by Sdore, 2019
|
Loading…
x
Reference in New Issue
Block a user