├── tools ├── plugins │ └── vscode │ │ └── skiars.berry-1.2.0 │ │ ├── README.md │ │ ├── berry-icon.png │ │ ├── CHANGELOG.md │ │ ├── package.json │ │ ├── berry-configuration.json │ │ └── .vsixmanifest ├── grammar │ ├── json.ebnf │ ├── const_obj.ebnf │ ├── berry.ebnf │ └── berry.bytecode ├── coc │ ├── coc_string_test.py │ ├── bytes_build.py │ ├── coc_string.py │ ├── macro_table.py │ ├── coc │ └── block_builder.py └── highlighters │ └── Pygments │ └── berry.py ├── berry-logo.png ├── .github └── workflows │ ├── codeql-buildscript.sh │ ├── c-cpp.yml │ └── codeql.yml ├── examples ├── json.be ├── fib_rec.be ├── lambda.be ├── exception.be ├── strmod.be ├── calcpi.be ├── bigloop.be ├── listdir.be ├── anon_func.be ├── guess_number.be ├── string.be ├── bintree.be ├── repl.be └── qsort.be ├── tests ├── exceptions.be ├── overload.be ├── function.be ├── json_test_stack_size.be ├── bitwise.be ├── int.be ├── vararg.be ├── cond_expr.be ├── closure.be ├── lexergc.be ├── bytes_b64.be ├── range.be ├── compound.be ├── parser.be ├── comptr.be ├── assignment.be ├── reference.be ├── suffix.be ├── debug.be ├── for.be ├── map.be ├── introspect_ismethod.be ├── checkspace.be ├── relop.be ├── division_by_zero.be ├── subobject.be ├── json_advanced.be ├── super_leveled.be ├── virtual_methods2.be ├── module.be ├── class.be ├── member_indirect.be ├── bool.be ├── global.be ├── virtual_methods.be ├── os.be ├── lexer.be ├── introspect.be ├── bytes_fixed.be ├── compiler.be ├── math.be ├── walrus.be ├── class_const.be ├── json.be ├── super_auto.be ├── call.be ├── class_static.be └── list.be ├── src ├── be_libs.h ├── be_repl.h ├── be_bytecode.h ├── be_debug.h ├── be_libs.c ├── be_func.h ├── be_syslib.c ├── be_baselib.h ├── be_undefinedlib.c ├── be_strlib.h ├── be_var.h ├── be_strictlib.c ├── be_gclib.c ├── be_mem.h ├── be_sys.h ├── be_module.h ├── be_list.h ├── be_string.h ├── be_map.h ├── be_exec.h ├── be_timelib.c ├── be_vector.h ├── be_code.h ├── be_byteslib.h ├── be_object.c ├── be_parser.h ├── be_decoder.h ├── be_class.h ├── be_gc.h ├── be_opcodes.h ├── be_repl.c ├── be_globallib.c ├── be_lexer.h ├── be_vector.c ├── be_func.c └── be_rangelib.c ├── .gitignore ├── modules ├── examples │ └── binary_heap_sort.be └── binary_heap.be ├── LICENSE ├── testall.be ├── CMakeLists.txt ├── default └── be_modtab.c ├── Makefile └── README.md /tools/plugins/vscode/skiars.berry-1.2.0/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /berry-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/berry-lang/berry/HEAD/berry-logo.png -------------------------------------------------------------------------------- /.github/workflows/codeql-buildscript.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | sudo apt-get install -y libreadline-dev 4 | make 5 | -------------------------------------------------------------------------------- /tools/plugins/vscode/skiars.berry-1.2.0/berry-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/berry-lang/berry/HEAD/tools/plugins/vscode/skiars.berry-1.2.0/berry-icon.png -------------------------------------------------------------------------------- /examples/json.be: -------------------------------------------------------------------------------- 1 | import json 2 | print(json.load('{"key": "value"}')) 3 | print(json.dump({'test key': nil})) 4 | print(json.dump({'key1': nil, 45: true}, 'format')) 5 | -------------------------------------------------------------------------------- /tests/exceptions.be: -------------------------------------------------------------------------------- 1 | 2 | try 3 | for k: 0..1 assert({'a':1}.contains('b'), 'failure') end 4 | except .. as e,m 5 | assert(e == "assert_failed") 6 | assert(m == "failure") 7 | end 8 | -------------------------------------------------------------------------------- /tools/grammar/json.ebnf: -------------------------------------------------------------------------------- 1 | json = value; 2 | value = object | array | 3 | string | number | 'true' | 'false' | 'null'; 4 | object = '{' [ string ':' value ] { ',' string ':' value } '}'; 5 | array = '[' [json] { ',' json } ']'; 6 | -------------------------------------------------------------------------------- /tests/overload.be: -------------------------------------------------------------------------------- 1 | class test 2 | def init() 3 | self._a = 123 4 | end 5 | def +() 6 | return self._a 7 | end 8 | def ()() 9 | return self._a 10 | end 11 | var _a 12 | end 13 | 14 | print(test() + test()) 15 | -------------------------------------------------------------------------------- /tests/function.be: -------------------------------------------------------------------------------- 1 | # CLOSE opcode test 2 | var gbl 3 | def func1() 4 | var a = 'func1_a' 5 | def func2() 6 | return a 7 | end 8 | gbl = func2 9 | return 400000 + 500 10 | end 11 | assert(func1() == 400500) 12 | assert(gbl() == 'func1_a') 13 | -------------------------------------------------------------------------------- /examples/fib_rec.be: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def fib(x) 4 | if x <= 2 5 | return 1 6 | end 7 | return fib(x - 1) + fib(x - 2) 8 | end 9 | 10 | c = time.clock() 11 | print("fib:", fib(38)) # minimum stack size: 78!! 12 | print("time:", time.clock() - c, 's') 13 | -------------------------------------------------------------------------------- /examples/lambda.be: -------------------------------------------------------------------------------- 1 | # simple lambda example 2 | print((/a b c-> a * b + c)(2, 3, 4)) 3 | 4 | # Y-Combinator and factorial functions 5 | Y = /f-> (/x-> f(/n-> x(x)(n)))(/x-> f(/n-> x(x)(n))) 6 | F = /f-> /x-> x ? f(x - 1) * x : 1 7 | fact = Y(F) 8 | print('fact(10) == ' .. fact(10)) 9 | -------------------------------------------------------------------------------- /examples/exception.be: -------------------------------------------------------------------------------- 1 | import debug 2 | 3 | def test_func() 4 | try 5 | compile('def +() end')() 6 | except .. as e, v 7 | print('catch execption:', str(e) + ' >>>\n ' + str(v)) 8 | debug.traceback() 9 | end 10 | end 11 | 12 | test_func() 13 | -------------------------------------------------------------------------------- /examples/strmod.be: -------------------------------------------------------------------------------- 1 | import string 2 | 3 | print(string.format('%.3d', 12)) 4 | print(string.format('%.3f', 12)) 5 | print(string.format('%20.7f', 14.5)) 6 | print(string.format('-- %-40s ---', 'this is a string format test')) 7 | print(string.format('-- %40s ---', 'this is a string format test')) 8 | -------------------------------------------------------------------------------- /tests/json_test_stack_size.be: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | # this test must be in a separate file, so that the stack is not expanded yet by other tests 4 | 5 | arr = "{" 6 | for i : 0..1000 7 | arr += '"k' + str(i) + '": "v' + str(i) + '",' 8 | end 9 | arr += "}" 10 | 11 | json.load(arr) 12 | -------------------------------------------------------------------------------- /tests/bitwise.be: -------------------------------------------------------------------------------- 1 | # and, or, xor 2 | a = 11 3 | assert(a & 0xFE == 10) 4 | assert(a | 32 == 43) 5 | assert(a ^ 33 == 42) 6 | 7 | # same with literal 8 | assert(11 & 0xFE == 10) 9 | assert(11 | 32 == 43) 10 | assert(11 ^ 33 == 42) 11 | 12 | # flip 13 | assert(~a == -12) 14 | assert(~11 == -12) 15 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: configure 13 | run: sudo apt-get install libreadline-dev lcov 14 | - name: build & test 15 | run: make test 16 | -------------------------------------------------------------------------------- /examples/calcpi.be: -------------------------------------------------------------------------------- 1 | def cpi(n) 2 | i = 2 3 | pi = 3 4 | while i <= n 5 | term = 4.0 / (i * (i + 1) * (i + 2)) 6 | if i % 4 7 | pi = pi + term 8 | else 9 | pi = pi - term 10 | end 11 | i = i + 2 12 | end 13 | return pi 14 | end 15 | 16 | print("pi =", cpi(100)) 17 | -------------------------------------------------------------------------------- /examples/bigloop.be: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | c = time.clock() 4 | do 5 | i = 0 6 | while i < 100000000 7 | i += 1 8 | end 9 | end 10 | print('while iteration 100000000 times', time.clock() - c, 's') 11 | 12 | c = time.clock() 13 | for i : 1 .. 100000000 14 | end 15 | print('for iteration 100000000 times', time.clock() - c, 's') 16 | -------------------------------------------------------------------------------- /tests/int.be: -------------------------------------------------------------------------------- 1 | #- toint() converts any instance to int -# 2 | class Test_int 3 | def toint() 4 | return 42 5 | end 6 | end 7 | t=Test_int() 8 | assert(int(t) == 42) 9 | 10 | #- int can parse hex strings -# 11 | assert(int("0x00") == 0) 12 | assert(int("0X1") == 1) 13 | assert(int("0x000000F") == 15) 14 | assert(int("0x1000") == 0x1000) 15 | -------------------------------------------------------------------------------- /tests/vararg.be: -------------------------------------------------------------------------------- 1 | #- vararg -# 2 | def f(a,*b) return b end 3 | 4 | assert(f() == []) 5 | assert(f(1) == []) 6 | assert(f(1,2) == [2]) 7 | assert(f(1,2,3) == [2, 3]) 8 | 9 | def g(*a) return a end 10 | 11 | assert(g() == []) 12 | assert(g("foo") == ["foo"]) 13 | assert(g("foo", nil) == ["foo", nil]) 14 | assert(g("foo", nil, 2) == ["foo", nil, 2]) 15 | -------------------------------------------------------------------------------- /tests/cond_expr.be: -------------------------------------------------------------------------------- 1 | assert("" != 0 ? true : false) 2 | assert(false || !(true ? false : true) && true) 3 | var t1 = 8, t2 = false 4 | if t1 ? 7 + t1 : t2 5 | var a = 'good' 6 | assert((a == 'good' ? a + '!' : a) == 'good!') 7 | assert((a == 'good?' ? a + '!' : a) != 'good!') 8 | else 9 | assert('condition expression test failed') 10 | end 11 | -------------------------------------------------------------------------------- /tests/closure.be: -------------------------------------------------------------------------------- 1 | #- test for issue #105 -# 2 | 3 | l=[] 4 | def tick() 5 | var start=100 6 | for i : 1..3 7 | l.push(def () return [i, start] end) 8 | end 9 | end 10 | tick() 11 | assert(l[0]() == [1, 100]) 12 | assert(l[1]() == [2, 100]) 13 | assert(l[2]() == [3, 100]) 14 | 15 | # the following failed to compile #344 16 | def test() var nv = 1 var f = def() nv += 2*1 print(nv) end end 17 | -------------------------------------------------------------------------------- /tests/lexergc.be: -------------------------------------------------------------------------------- 1 | #- check the gc bug fixed in #110 -# 2 | #- Berry must be compiled with `#define BE_USE_DEBUG_GC 1` -# 3 | #- for the initial bug to happen -# 4 | 5 | code = "()" #- this code triggers a lexer exception -# 6 | 7 | try 8 | compile(code) 9 | assert(false) #- this should never be reached -# 10 | except .. as e, m 11 | assert(m == "string:1: unexpected symbol near ')'") 12 | end -------------------------------------------------------------------------------- /examples/listdir.be: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def scandir(path) 4 | print('path: ' + path) 5 | for name : os.listdir(path) 6 | var fullname = os.path.join(path, name) 7 | if os.path.isfile(fullname) 8 | print('file: ' + fullname) 9 | else 10 | print('path: ' + fullname) 11 | scandir(fullname) 12 | end 13 | end 14 | end 15 | 16 | scandir('.') 17 | -------------------------------------------------------------------------------- /tools/grammar/const_obj.ebnf: -------------------------------------------------------------------------------- 1 | block = type name ['(' {attributes} ')'] '{' {data_fields} '}'; 2 | type = 'map' | 'class' | 'module' | 'vartab'; 3 | 4 | attributes = name ':' name [',']; 5 | data_fields = data_name ',' data_value [':' depend_macro] '\n'; 6 | 7 | (* regular expression *) 8 | name = [_a-zA-Z]\w*; 9 | data_name = [\._a-zA-Z]\w*; 10 | data_value = [\w\()]+; 11 | depend_macro = [_a-zA-Z]\w*; 12 | -------------------------------------------------------------------------------- /tools/plugins/vscode/skiars.berry-1.2.0/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to the "none" extension will be documented in this file. 3 | 4 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. 5 | 6 | ## [Unreleased] 7 | - Initial release 8 | - add support for f-strings, `_class` and indent on `do` 9 | - add support for multiline f-strings 10 | - remove bytecode -------------------------------------------------------------------------------- /tests/bytes_b64.be: -------------------------------------------------------------------------------- 1 | #- base64 encode -# 2 | b=bytes() 3 | assert(b.tob64() == '') 4 | b=bytes('00') 5 | assert(b.tob64() == 'AA==') 6 | b=bytes('1122334455') 7 | assert(b.tob64() == 'ESIzRFU=') 8 | 9 | #- base64 decode -# 10 | b=bytes().fromb64('') 11 | assert(str(b) == str(bytes(''))) 12 | b=bytes().fromb64('AA==') 13 | assert(str(b) == str(bytes('00'))) 14 | b=bytes().fromb64('ESIzRFU=') 15 | assert(str(b) == str(bytes('1122334455'))) 16 | -------------------------------------------------------------------------------- /examples/anon_func.be: -------------------------------------------------------------------------------- 1 | # anonymous function and closure 2 | def count(x) 3 | var arr = [] 4 | for i : 0 .. x 5 | arr.push( 6 | def (n) # loop variable cannot be used directly as free variable 7 | return def () 8 | return n * n 9 | end 10 | end (i) # define and call anonymous function 11 | ) 12 | end 13 | return arr 14 | end 15 | 16 | for xx : count(6) 17 | print(xx()) # 0, 1, 4 ... n * n 18 | end 19 | 20 | return count 21 | -------------------------------------------------------------------------------- /src/be_libs.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_LIBS_H 9 | #define BE_LIBS_H 10 | 11 | #include "berry.h" 12 | 13 | void be_loadlibs(bvm *vm); 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /tests/range.be: -------------------------------------------------------------------------------- 1 | # test ranges 2 | var l 3 | 4 | # simple ranges 5 | l = [] 6 | for i:1..10 l..i end 7 | assert(l == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 8 | 9 | l = [] 10 | for i:range(1, 10) l..i end 11 | assert(l == [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) 12 | 13 | # range in higher steps 14 | l = [] 15 | for i:range(1, 10, 2) l..i end 16 | assert(l == [1, 3, 5, 7, 9]) 17 | 18 | # negative ranges 19 | l = [] 20 | for i:range(1, 10, -2) l..i end 21 | assert(l == []) 22 | 23 | l = [] 24 | for i:range(30, 0, -3) l..i end 25 | assert(l == [30, 27, 24, 21, 18, 15, 12, 9, 6, 3, 0]) 26 | -------------------------------------------------------------------------------- /tests/compound.be: -------------------------------------------------------------------------------- 1 | # test bug in compound statements 2 | 3 | a = 0 4 | assert(a == 0) 5 | a += 1 6 | assert(a == 1) 7 | a += 10/2 8 | assert(a == 6) 9 | 10 | class A var a def init() self.a = 1 end def f(x) self.a+=x/2 end def g(x) self.a = self.a + x/2 end end 11 | 12 | a = A() 13 | assert(a.a == 1) 14 | a.f(10) 15 | assert(a.a == 6) 16 | b=A() 17 | assert(b.a == 1) 18 | b.g(10) 19 | assert(b.a == 6) 20 | 21 | # bug in compound assignments 22 | class A var a,b end 23 | c=A() 24 | c.a = {"x": 1, "y": 2} 25 | c.b = "x" 26 | assert(c.a[c.b] == 1) 27 | c.a[c.b] += 2 # this is where the bug happens 28 | assert(c.a[c.b] == 3) 29 | -------------------------------------------------------------------------------- /tests/parser.be: -------------------------------------------------------------------------------- 1 | # Test some sparser specific bugs 2 | 3 | # https://github.com/berry-lang/berry/issues/396 4 | def f() 5 | if true 6 | var a = 1 7 | a = true ? a+1 : a+2 8 | return a 9 | end 10 | end 11 | assert(f() == 2) 12 | 13 | # Parser error reported in Feb 2025 14 | def parse_022025() 15 | var s, value 16 | var js = {'a':{'a':1}} 17 | value = js['a']['a'] 18 | 19 | if value != nil 20 | for x:0..1 21 | return x 22 | end 23 | end 24 | end 25 | assert(parse_022025() == 0) 26 | 27 | # bug #371 - fix infinite loop 28 | def f() 1 || print(2) end 29 | -------------------------------------------------------------------------------- /tests/comptr.be: -------------------------------------------------------------------------------- 1 | # test about comptr 2 | import introspect 3 | 4 | var p = introspect.toptr(1024) 5 | assert(str(p) == '') 6 | 7 | p += 1 8 | assert(p == introspect.toptr(1025)) 9 | 10 | p -= 2 11 | assert(p == introspect.toptr(1023)) 12 | 13 | # use comptr[idx] to read or write bytes 14 | var b = bytes("11223344") 15 | p = b._buffer() # p is comptr 16 | assert(p[0] == 0x11) 17 | assert(p[1] == 0x22) 18 | assert(p[2] == 0x33) 19 | assert(p[3] == 0x44) 20 | 21 | p[0] = 0xFF 22 | p[1] = 0x55 23 | p[2] = 0xFEBC # shoud truncate to 0xBC 24 | assert(b == bytes("FF55BC44")) 25 | assert(p[0] == 255) # check it's unsigned 26 | -------------------------------------------------------------------------------- /tests/assignment.be: -------------------------------------------------------------------------------- 1 | class Test 2 | var a 3 | end 4 | 5 | # continuous assignment of global suffix expressions 6 | o = Test() 7 | o.a = 100 8 | assert(o.a == 100) 9 | o.a += 10 10 | assert(o.a == 110) 11 | 12 | p = Test() 13 | p.a = Test() 14 | p.a.a = 50 15 | assert(p.a.a == 50) 16 | p.a.a += 10 17 | assert(p.a.a == 60) 18 | 19 | # continuous assignment of local suffix expressions 20 | def test_func() 21 | var o = Test() 22 | o.a = 100 23 | assert(o.a == 100) 24 | o.a += 10 25 | assert(o.a == 110) 26 | 27 | var p = Test() 28 | p.a = Test() 29 | p.a.a = 50 30 | assert(p.a.a == 50) 31 | p.a.a += 10 32 | assert(p.a.a == 60) 33 | end 34 | test_func() 35 | -------------------------------------------------------------------------------- /tools/coc/coc_string_test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from coc_string import * 3 | 4 | class Test_coc_string(unittest.TestCase): 5 | 6 | def test_hash(self): 7 | self.assertEqual(hashcode("add"), 993596020) 8 | self.assertEqual(hashcode("real"), 3604983901) 9 | 10 | def test_escape(self): 11 | self.assertEqual(escape_operator("foo"), "foo") 12 | self.assertEqual(escape_operator(".."), "_opt_connect") 13 | self.assertEqual(escape_operator("foo.bar"), "foo_dot_bar") 14 | self.assertEqual(escape_operator("foo._bar"), "foo_dot___bar") 15 | self.assertEqual(escape_operator("foo. bar"), "foo_dot__20bar") 16 | 17 | if __name__ == '__main__': 18 | unittest.main() -------------------------------------------------------------------------------- /tests/reference.be: -------------------------------------------------------------------------------- 1 | # try to exercise bug in reference 2 | 3 | class failable 4 | var fail # if 'true', tostring() raises an exception 5 | 6 | def tostring() 7 | if self.fail 8 | raise "internal_error", "FAIL" 9 | return "FAIL" 10 | else 11 | return "SUCCESS" 12 | end 13 | end 14 | end 15 | f = failable() 16 | 17 | l1 = [1, 2, f] 18 | l2 = ["foo", l1] 19 | l1.push(l1) 20 | 21 | assert(str(l2) == "['foo', [1, 2, SUCCESS, [...]]]") 22 | assert(str(l1) == "[1, 2, SUCCESS, [...]]") 23 | 24 | f.fail = true 25 | try 26 | print(str(l1)) 27 | except .. 28 | end 29 | 30 | f.fail = false 31 | assert(str(l1) == "[1, 2, SUCCESS, [...]]") # FAILS 32 | -------------------------------------------------------------------------------- /tests/suffix.be: -------------------------------------------------------------------------------- 1 | var keys = [ 'key1', 'key2', 'key3', 'key4' ] 2 | var pairs = { 3 | keys[0]: 'value1', 4 | keys[1]: 'value2', 5 | keys[2]: 'value3', 6 | keys[3]: 'value4' 7 | } 8 | 9 | for i : 0 .. keys.size() - 1 10 | assert(pairs[keys[i]] == 'value' .. i + 1) 11 | end 12 | 13 | #- test cases related to #101 -# 14 | class C var l end 15 | c=C() 16 | c.l=[0,1,2] 17 | 18 | def t_101_nok_1() return c.l[0..1] end 19 | def t_101_ok_1() var l2 = c.l return l2[0..1] end 20 | 21 | t_i = 0 22 | def t_101_nok_2() return c.l[t_i] end 23 | def t_101_ok_2() return c.l[0] end 24 | 25 | assert(t_101_nok_1() == [0, 1]) 26 | assert(t_101_ok_1() == [0, 1]) 27 | assert(t_101_nok_2() == 0) 28 | assert(t_101_ok_2() == 0) 29 | -------------------------------------------------------------------------------- /tests/debug.be: -------------------------------------------------------------------------------- 1 | import debug 2 | 3 | class A end 4 | debug.attrdump(A) #- should not crash -# 5 | 6 | # debug.caller() 7 | def caller_name_chain() 8 | import debug 9 | import introspect 10 | var i = 1 11 | var ret = [] 12 | var caller = debug.caller(i) 13 | while caller 14 | ret.push(introspect.name(caller)) 15 | i += 1 16 | caller = debug.caller(i) 17 | end 18 | return ret 19 | end 20 | var chain = caller_name_chain() 21 | assert(chain[0] == 'caller_name_chain') 22 | 23 | def guess_my_name__() 24 | return caller_name_chain() 25 | end 26 | chain = guess_my_name__() 27 | print(chain) 28 | assert(chain[0] == 'caller_name_chain') 29 | assert(chain[1] == 'guess_my_name__') 30 | -------------------------------------------------------------------------------- /examples/guess_number.be: -------------------------------------------------------------------------------- 1 | import time 2 | import math 3 | 4 | math.srand(time.time()) 5 | res = math.rand() % 100 6 | max_test = 7 7 | test = -1 8 | idx = 1 9 | print('Guess a number between 0 and 99. You have', max_test, 'chances.') 10 | while test != res && idx <= max_test 11 | test = number(input(str(idx) + ': enter the number you guessed: ')) 12 | if type(test) != 'int' 13 | print('This is not an integer. Continue!') 14 | continue 15 | elif test > res 16 | print('This number is too large.') 17 | elif test < res 18 | print('This number is too small.') 19 | end 20 | idx = idx + 1 21 | end 22 | if test == res 23 | print('You win!') 24 | else 25 | print('You failed, the correct answer is', res) 26 | end 27 | -------------------------------------------------------------------------------- /examples/string.be: -------------------------------------------------------------------------------- 1 | s = "This is a long string test. 0123456789 abcdefg ABCDEFG" 2 | print(s) 3 | 4 | a = .5 5 | print(a) 6 | 7 | import string as s 8 | 9 | print(s.hex(0x45678ABCD, 16)) 10 | 11 | def bin(x, num) 12 | assert(type(x) == 'int', 'the type of \'x\' must be integer') 13 | # test the 'x' bits 14 | var bits = 1 15 | for i : 0 .. 62 16 | if x & (1 << 63 - i) 17 | bits = 64 - i 18 | break 19 | end 20 | end 21 | if type(num) == 'int' && num > 0 && num <= 64 22 | bits = bits < num ? num : bits 23 | end 24 | var result = '' 25 | bits -= 1 26 | for i : 0 .. bits 27 | result += x & (1 << (bits - i)) ? '1' : '0' 28 | end 29 | return result 30 | end 31 | 32 | print(bin(33)) 33 | -------------------------------------------------------------------------------- /src/be_repl.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_REPL_H 9 | #define BE_REPL_H 10 | 11 | #include "berry.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | typedef char* (*breadline)(const char *prompt); 18 | typedef void (*bfreeline)(char *ptr); 19 | 20 | BERRY_API int be_repl(bvm *vm, breadline getline, bfreeline freeline); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /tests/for.be: -------------------------------------------------------------------------------- 1 | var global 2 | 3 | global = 0 4 | for i : 0 .. 10 5 | global += i 6 | end 7 | assert(global == 55) 8 | 9 | global = 0 10 | for i : 0 .. 20 11 | if i > 10 12 | break 13 | end 14 | global += i 15 | end 16 | assert(global == 55) 17 | 18 | global = 0 19 | for i : 0 .. 20 20 | if i > 10 21 | continue 22 | end 23 | global += i 24 | end 25 | assert(global == 55) 26 | 27 | assert(def () 28 | for i : 0 .. 20 29 | if i > 10 30 | return i 31 | end 32 | end 33 | end() == 11) 34 | 35 | # test for "stop_iteration" exception as recurrence 36 | def for_rec(depth) 37 | for i : 0 .. 10 38 | if i == 4 && depth < 200 39 | for_rec(depth + 1) 40 | end 41 | end 42 | end 43 | 44 | for_rec(0) 45 | -------------------------------------------------------------------------------- /src/be_bytecode.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef __BE_BYTECODE_H 9 | #define __BE_BYTECODE_H 10 | 11 | #include "be_object.h" 12 | 13 | void be_bytecode_save(bvm *vm, const char *filename, bproto *proto); 14 | void be_bytecode_save_to_fs(bvm *vm, void *fp, bproto *proto); 15 | bclosure* be_bytecode_load(bvm *vm, const char *filename); 16 | bclosure* be_bytecode_load_from_fs(bvm *vm, void *fp); 17 | bbool be_bytecode_check(const char *path); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /tests/map.be: -------------------------------------------------------------------------------- 1 | m = { 'a':1, 'b':3.5, 'c': "foo", 0:1} 2 | 3 | assert(type(m) == 'instance') 4 | assert(classname(m) == 'map') 5 | 6 | # accessor 7 | assert(m['a'] == 1) 8 | assert(m['b'] == 3.5) 9 | assert(m['c'] == 'foo') 10 | assert(m[0] == 1) 11 | 12 | # find 13 | assert(m.find('a') == 1) 14 | assert(m.find('z') == nil) 15 | assert(m.find('z', 4) == 4) 16 | 17 | # contains 18 | assert(m.contains('a')) 19 | assert(m.contains(0)) 20 | assert(!m.contains('z')) 21 | assert(!m.contains()) 22 | 23 | # set 24 | m['y'] = -1 25 | assert(m['y'] == -1) 26 | 27 | # remove 28 | m={1:2} 29 | m.remove(2) 30 | assert(str(m) == '{1: 2}') 31 | m.remove(1) 32 | assert(str(m) == '{}') 33 | 34 | # allow booleans to be used as keys 35 | m={true:10, false:20} 36 | assert(m.contains(true)) 37 | assert(m.contains(false)) 38 | assert(m[true] == 10) 39 | assert(m[false] == 20) 40 | -------------------------------------------------------------------------------- /tests/introspect_ismethod.be: -------------------------------------------------------------------------------- 1 | # test for introspect.ismethod 2 | import introspect 3 | 4 | # ismethod should return nil for any non-Berry closure 5 | assert(introspect.ismethod() == nil) 6 | assert(introspect.ismethod(true) == nil) 7 | assert(introspect.ismethod("a") == nil) 8 | assert(introspect.ismethod([]) == nil) 9 | assert(introspect.ismethod({}) == nil) 10 | assert(introspect.ismethod(introspect) == nil) # module 11 | assert(introspect.ismethod(introspect.ismethod) == nil) # native method 12 | 13 | def h() end 14 | 15 | class A 16 | def f() end 17 | static def g() end 18 | var h 19 | end 20 | a=A() 21 | a.h = h 22 | 23 | assert(introspect.ismethod(h) == false) 24 | assert(introspect.ismethod(A.f) == true) 25 | assert(introspect.ismethod(A.g) == false) 26 | 27 | assert(introspect.ismethod(a.f) == true) 28 | assert(introspect.ismethod(a.g) == false) 29 | assert(introspect.ismethod(a.h) == false) 30 | -------------------------------------------------------------------------------- /src/be_debug.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_DEBUG_H 9 | #define BE_DEBUG_H 10 | 11 | #include "be_object.h" 12 | 13 | struct bhookblock { 14 | void *data; 15 | bntvhook hook; 16 | }; 17 | 18 | void be_dumpclosure(bclosure *cl); 19 | void be_tracestack(bvm *vm); 20 | void be_callhook(bvm *vm, int mask); 21 | bbool be_debug_varname(bvm *vm, int level, int index); 22 | bbool be_debug_upvname(bvm *vm, int level, int index); 23 | 24 | #if BE_USE_DEBUG_MODULE 25 | void be_print_inst(binstruction ins, int pc, void* fout); 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /tests/checkspace.be: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def strfind(st, char) 4 | var len = size(st) 5 | for i : 0 .. len - 1 6 | if st[i] == char 7 | return true 8 | end 9 | end 10 | return false 11 | end 12 | 13 | def checkfile(path) 14 | var subname = os.path.splitext(path)[1] 15 | if (subname == '.c' || subname == '.h' || 16 | subname == '.cpp' || subname == '.be' || subname == '.json') 17 | var f = open(path) 18 | assert(!strfind(f.read(), '\t'), 'file \'' + path + '\' has tab character') 19 | f.close() 20 | end 21 | end 22 | 23 | def findpath(path) 24 | var ls = os.listdir(path) 25 | for name : ls 26 | var fullname = os.path.join(path, name) 27 | if os.path.isfile(fullname) 28 | checkfile(fullname) 29 | elif fullname != '.' && fullname != '..' 30 | findpath(fullname) 31 | end 32 | end 33 | end 34 | 35 | findpath('.') 36 | -------------------------------------------------------------------------------- /tests/relop.be: -------------------------------------------------------------------------------- 1 | def assert_true(status) 2 | assert(status == true, 'assert(true) failed!') 3 | end 4 | 5 | def assert_false(status) 6 | assert(status == false, 'assert(false) failed!') 7 | end 8 | 9 | assert_true(0 == 0) 10 | assert_false(0 != 0) 11 | assert_true(0 != 1) 12 | assert_false(0 == 1) 13 | 14 | 15 | assert_true(0.0 == 0) 16 | assert_false(0.0 != 0) 17 | assert_true(0.0 != 1.0) 18 | assert_false(0.0 == 1.0) 19 | 20 | assert_true(nil == nil) 21 | assert_false(nil != nil) 22 | assert_true(true != nil) 23 | assert_false(true == nil) 24 | assert_true(nil != false) 25 | assert_false(nil == false) 26 | 27 | assert_true(list == list) 28 | assert_false(list == map) 29 | 30 | assert_true([] == []) 31 | assert_true([true] == [true]) 32 | assert_true([[]] == [[]]) 33 | assert_false([[]] != [[]]) 34 | assert_false([0] == []) 35 | assert_false([] != []) 36 | assert_true([] != nil) 37 | assert_false([] == nil) 38 | 39 | assert_true({} != nil) 40 | assert_false({} == nil) 41 | -------------------------------------------------------------------------------- /tests/division_by_zero.be: -------------------------------------------------------------------------------- 1 | 2 | try 3 | # Test integer division 4 | var div = 1/0 5 | assert(false) # Should not reach this point 6 | except .. as e,m 7 | 8 | assert(e == "divzero_error") 9 | assert(m == "division by zero") 10 | end 11 | 12 | 13 | try 14 | # Test integer modulo 15 | var div = 1%0 16 | assert(false) 17 | except .. as e,m 18 | assert(e == "divzero_error") 19 | assert(m == "division by zero") 20 | end 21 | 22 | try 23 | # Test float division 24 | var div = 1.1/0.0 25 | assert(false) 26 | except .. as e,m 27 | assert(e == "divzero_error") 28 | assert(m == "division by zero") 29 | end 30 | 31 | try 32 | # Test float modulo 33 | var div = 1.1%0.0 34 | assert(false) 35 | except .. as e,m 36 | assert(e == "divzero_error") 37 | assert(m == "division by zero") 38 | end 39 | 40 | 41 | # Check normal division & modulo 42 | assert(1/2 == 0) 43 | assert(1%2 == 1) 44 | assert(1.0/2.0 == 0.5) 45 | assert(1.0%2.0 == 1.0) 46 | assert(4/2 == 2) 47 | assert(4%2 == 0) 48 | -------------------------------------------------------------------------------- /tests/subobject.be: -------------------------------------------------------------------------------- 1 | class mylist : classof([]) end 2 | 3 | assert(issubclass(mylist, list) == true) 4 | assert(issubclass(mylist, []) == true) 5 | assert(issubclass(mylist(), list) == false) 6 | assert(issubclass(mylist(), []) == false) 7 | 8 | assert(isinstance(mylist, list) == false) 9 | assert(isinstance(mylist, []) == false) 10 | assert(isinstance(mylist(), list) == true) 11 | assert(isinstance(mylist(), []) == true) 12 | 13 | assert(issubclass(list, list) == true) 14 | assert(issubclass(list, []) == true) 15 | assert(issubclass(list(), list) == false) 16 | assert(issubclass(list(), []) == false) 17 | 18 | assert(isinstance(list, list) == false) 19 | assert(isinstance(list, []) == false) 20 | assert(isinstance(list(), list) == true) 21 | assert(isinstance(list(), []) == true) 22 | 23 | assert(issubclass(list, list) == true) 24 | assert(issubclass(list, []) == true) 25 | assert(issubclass(list(), list) == false) 26 | assert(issubclass(list(), []) == false) 27 | 28 | assert(issubclass(list, mylist) == false) 29 | assert(isinstance([], mylist) == false) 30 | -------------------------------------------------------------------------------- /src/be_libs.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_libs.h" 9 | 10 | extern void be_load_baselib(bvm *vm); 11 | extern void be_load_baselib_next(bvm *vm); 12 | extern void be_load_listlib(bvm *vm); 13 | extern void be_load_maplib(bvm *vm); 14 | extern void be_load_rangelib(bvm *vm); 15 | extern void be_load_filelib(bvm *vm); 16 | extern void be_load_byteslib(bvm *vm); 17 | 18 | void be_loadlibs(bvm *vm) 19 | { 20 | be_load_baselib(vm); 21 | #if !BE_USE_PRECOMPILED_OBJECT 22 | be_load_listlib(vm); 23 | be_load_maplib(vm); 24 | be_load_rangelib(vm); 25 | be_load_filelib(vm); 26 | be_load_byteslib(vm); 27 | be_load_baselib_next(vm); 28 | #endif 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | /berry 39 | *.bec 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | .gdb_history 47 | 48 | # Kernel Module Compile Results 49 | *.mod* 50 | *.cmd 51 | .tmp_versions/ 52 | modules.order 53 | Module.symvers 54 | Mkfile.old 55 | dkms.conf 56 | 57 | # Test files 58 | *.gcda 59 | *.gcno 60 | *.info 61 | test_report/ 62 | 63 | # Ctags 64 | tags 65 | 66 | # other 67 | *.out.* 68 | *.user 69 | .vscode 70 | .DS_Store 71 | generate/ 72 | temp/ 73 | .idea 74 | __pycache__ 75 | .cproject 76 | .project 77 | .settings/ 78 | docs/build/html/ 79 | docs/_doxygen/xml/ 80 | build/ 81 | cmake-build-* 82 | .cache -------------------------------------------------------------------------------- /src/be_func.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_FUNC_H 9 | #define BE_FUNC_H 10 | 11 | #include "be_object.h" 12 | 13 | #define be_newntvclos(vm, cf) \ 14 | be_newntvclosure(vm, cf, 0) 15 | 16 | #define be_ntvclos_upval(cc, n) \ 17 | (((bupval**)((size_t)cc + sizeof(bntvclos)))[n]) 18 | 19 | void be_initupvals(bvm *vm, bclosure *cl); 20 | void be_upvals_close(bvm *vm, bvalue *level); 21 | void be_release_upvalues(bvm *vm, bclosure *cl); 22 | bproto* be_newproto(bvm *vm); 23 | bclosure* be_newclosure(bvm *vm, int nupval); 24 | bntvclos* be_newntvclosure(bvm *vm, bntvfunc cf, int nupvals); 25 | bstring* be_func_varname(bproto *proto, int index, int pc); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /tools/plugins/vscode/skiars.berry-1.2.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "berry", 3 | "displayName": "Berry Script Language", 4 | "description": "A small embedded script language.", 5 | "version": "1.2.0", 6 | "icon": "berry-icon.png", 7 | "publisher": "skiars", 8 | "engines": { 9 | "vscode": "^1.15.1" 10 | }, 11 | "categories": [ 12 | "Programming Languages" 13 | ], 14 | "contributes": { 15 | "languages": [ 16 | { 17 | "id": "berry", 18 | "aliases": [ 19 | "Berry", 20 | "berry" 21 | ], 22 | "extensions": [ 23 | ".be" 24 | ], 25 | "configuration": "./berry-configuration.json" 26 | } 27 | ], 28 | "grammars": [ 29 | { 30 | "language": "berry", 31 | "scopeName": "source.berry", 32 | "path": "./syntaxes/berry.json" 33 | } 34 | ] 35 | }, 36 | "__metadata": null 37 | } -------------------------------------------------------------------------------- /tools/plugins/vscode/skiars.berry-1.2.0/berry-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "#", 5 | "blockComment": [ "#-", "-#" ] 6 | }, 7 | // symbols used as brackets 8 | "brackets": [ 9 | ["[", "]"], 10 | ["(", ")"], 11 | ["{", "}"] 12 | ], 13 | // symbols that are auto closed when typing 14 | "autoClosingPairs": [ 15 | ["[", "]"], 16 | ["(", ")"], 17 | ["{", "}"], 18 | ["\"", "\""], 19 | ["'", "'"] 20 | ], 21 | // symbols that that can be used to surround a selection 22 | "surroundingPairs": [ 23 | ["[", "]"], 24 | ["(", ")"], 25 | ["\"", "\""], 26 | ["'", "'"] 27 | ], 28 | "indentationRules": { 29 | "increaseIndentPattern": "(^((\\s*(class|while|for|if|elif|else|try|except|do))|(.*\\bdef))\\b((?!\\b(end)\\b).)*)$", 30 | "decreaseIndentPattern": "^\\s*((\\b(elif|else|except|end)\\b)|(\\)))" 31 | } 32 | } -------------------------------------------------------------------------------- /src/be_syslib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | 10 | #if BE_USE_SYS_MODULE 11 | 12 | static int m_path(bvm *vm) 13 | { 14 | be_getbuiltin(vm, "list"); 15 | be_module_path(vm); 16 | be_call(vm, 1); 17 | be_pop(vm, 1); 18 | be_return(vm); 19 | } 20 | 21 | #if !BE_USE_PRECOMPILED_OBJECT 22 | be_native_module_attr_table(sys){ 23 | be_native_module_function("path", m_path) 24 | }; 25 | 26 | be_define_native_module(sys, NULL); 27 | #else 28 | /* @const_object_info_begin 29 | module sys (scope: global, depend: BE_USE_SYS_MODULE) { 30 | path, func(m_path) 31 | } 32 | @const_object_info_end */ 33 | #include "../generate/be_fixed_sys.h" 34 | #endif 35 | 36 | #endif /* BE_USE_SYS_MODULE */ 37 | -------------------------------------------------------------------------------- /modules/examples/binary_heap_sort.be: -------------------------------------------------------------------------------- 1 | import binary_heap 2 | 3 | var pi = [3,14,15,92,65,35,89,79,32,38,46,26,43,38,32,79,50,28,84,19,71,69,39] 4 | # Display the elements of pi[] in ascending order 5 | var h = pi.copy() 6 | var less = / a b -> a < b, sort = binary_heap.sort 7 | sort(h, less) 8 | print(h) 9 | 10 | # Display the 5 largest elements of pi[] 11 | var cmp = / a b -> a > b 12 | var max = [] 13 | binary_heap.make_heap(h, cmp) 14 | var i = 5 15 | while i > 0 max.push(binary_heap.remove_heap(h, cmp)) i -= 1 end 16 | print(max) 17 | 18 | # Display the 7 largest elements of pi[] in ascending order of index 19 | h = [] 20 | i = 1 21 | while i < pi.size() h.push(i - 1) i += 1 end 22 | cmp = / a b -> pi[a] > pi[b] 23 | if false # less efficient alternative: sort the entire array 24 | sort(h, cmp) 25 | max = h[0..6] 26 | i = 0 27 | else # more efficient: only sort the minimum necessary subset 28 | max = [] 29 | binary_heap.make_heap(h, cmp) 30 | i = 7 31 | while i > 0 max.push(binary_heap.remove_heap(h, cmp)) i -= 1 end 32 | end 33 | sort(max, less) 34 | while i < 7 print(f"{max[i]}: {pi[max[i]]}") i += 1 end 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-2020 Guan Wenliang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/be_baselib.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef __BE_BASELIB_H 9 | #define __BE_BASELIB_H 10 | 11 | #include "be_object.h" 12 | 13 | int be_baselib_assert(bvm *vm); 14 | int be_baselib_print(bvm *vm); 15 | int be_baselib_input(bvm *vm); 16 | int be_baselib_super(bvm *vm); 17 | int be_baselib_type(bvm *vm); 18 | int be_baselib_classname(bvm *vm); 19 | int be_baselib_classof(bvm *vm); 20 | int be_baselib_number(bvm *vm); 21 | int be_baselib_str(bvm *vm); 22 | int be_baselib_int(bvm *vm); 23 | int be_baselib_real(bvm *vm); 24 | int be_baselib_module(bvm *vm); 25 | int be_baselib_size(bvm *vm); 26 | int be_baselib_compile(bvm *vm); 27 | int be_baselib_issubclass(bvm *vm); 28 | int be_baselib_isinstance(bvm *vm); 29 | int be_baselib_iterator(bvm *vm); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /src/be_undefinedlib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang, Stephan Hadinger 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | #include "be_module.h" 10 | #include "be_string.h" 11 | #include "be_vector.h" 12 | #include "be_class.h" 13 | #include "be_debug.h" 14 | #include "be_map.h" 15 | #include "be_vm.h" 16 | #include "be_exec.h" 17 | #include "be_gc.h" 18 | #include 19 | 20 | 21 | #if !BE_USE_PRECOMPILED_OBJECT 22 | be_native_module_attr_table(undefined) { 23 | be_native_module_nil(".p"), /* not needed but can't be empty */ 24 | }; 25 | 26 | be_define_native_module(undefined, NULL); 27 | #else 28 | /* @const_object_info_begin 29 | module undefined (scope: global) { 30 | .p, nil() 31 | } 32 | @const_object_info_end */ 33 | #include "../generate/be_fixed_undefined.h" 34 | #endif 35 | -------------------------------------------------------------------------------- /testall.be: -------------------------------------------------------------------------------- 1 | #! ./berry 2 | import os 3 | 4 | os.system('lcov', '-q -c -i -d . -o init.info') 5 | 6 | var exec = './berry' 7 | var path = 'tests' 8 | var testcases = os.listdir(path) 9 | var total = 0, failed = 0 10 | 11 | for i : testcases 12 | if os.path.splitext(i)[1] == '.be' 13 | print('\033[0;36mrun testcase: ' + i + '\033[0m') 14 | var ret = os.system(exec, os.path.join(path, i)) 15 | if ret != 0 16 | print('\033[0;31mreturn code:', ret, '\033[0m') 17 | failed += 1 18 | end 19 | total += 1 20 | end 21 | end 22 | 23 | print('\033[0;32mtest results: ' + 24 | str(total) + ' total, ' + str(failed) + ' failed' + 25 | (failed ? '' : ' (all tests passed)') + 26 | '.\033[0m') 27 | 28 | if failed != 0 29 | os.exit(-1) 30 | end 31 | 32 | var cmds = [ 33 | 'lcov -q -c -d ./ -o cover.info', 34 | 'lcov -q -a init.info -a cover.info -o total.info', 35 | 'lcov --remove total.info */usr/include/* -o final.info', 36 | 'genhtml -q -o test_report --legend --title "lcov" --prefix=./ final.info', 37 | 'rm -f init.info cover.info total.info final.info' 38 | ] 39 | 40 | for cmd : cmds 41 | if os.system(cmd) 42 | os.exit(-1) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /src/be_strlib.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_STRLIB_H 9 | #define BE_STRLIB_H 10 | 11 | #include "be_object.h" 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | bstring* be_strcat(bvm *vm, bstring *s1, bstring *s2); 19 | bstring* be_strmul(bvm *vm, bstring *s1, bint n); 20 | int be_strcmp(bstring *s1, bstring *s2); 21 | bstring* be_num2str(bvm *vm, bvalue *v); 22 | void be_val2str(bvm *vm, int index); 23 | int be_char2hex(int c); 24 | size_t be_strlcpy(char *dst, const char *src, size_t size); 25 | const char* be_splitpath(const char *path); 26 | const char* be_splitname(const char *path); 27 | const char* be_pushvfstr(bvm *vm, const char *format, va_list arg); 28 | bstring* be_strindex(bvm *vm, bstring *str, bvalue *idx); 29 | int be_str_format(bvm *vm); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/be_var.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_VAR_H 9 | #define BE_VAR_H 10 | 11 | #include "be_object.h" 12 | 13 | #define be_global_count(vm) \ 14 | be_vector_count(&(vm)->gbldesc.global.vlist) 15 | 16 | #define be_builtin_count(vm) \ 17 | be_vector_count(&(vm)->gbldesc.builtin.vlist) 18 | 19 | void be_globalvar_init(bvm *vm); 20 | void be_globalvar_deinit(bvm *vm); 21 | int be_global_find(bvm *vm, bstring *name); 22 | int be_global_new(bvm *vm, bstring *name); 23 | bbool be_global_undef(bvm *vm, bstring *name); 24 | bvalue* be_global_var(bvm *vm, int index); 25 | void be_global_release_space(bvm *vm); 26 | int be_builtin_find(bvm *vm, bstring *name); 27 | bstring* be_builtin_name(bvm *vm, int index); 28 | int be_builtin_new(bvm *vm, bstring *name); 29 | void be_bulitin_release_space(bvm *vm); 30 | void be_const_builtin_set(bvm *vm, const bmap *map, const bvector *vec); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/be_strictlib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2021 Guan Wenliang & Stephan Hadinger 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | #include "be_module.h" 10 | #include "be_string.h" 11 | #include "be_vector.h" 12 | #include "be_class.h" 13 | #include "be_debug.h" 14 | #include "be_map.h" 15 | #include "be_vm.h" 16 | 17 | #if BE_USE_STRICT_MODULE 18 | 19 | static int m_init(bvm *vm) 20 | { 21 | comp_set_strict(vm); /* enable compiler strict mode */ 22 | be_return_nil(vm); 23 | } 24 | 25 | #if !BE_USE_PRECOMPILED_OBJECT 26 | be_native_module_attr_table(strict) { 27 | be_native_module_function("init", m_init), 28 | }; 29 | 30 | be_define_native_module(strict, NULL); 31 | #else 32 | /* @const_object_info_begin 33 | module strict (scope: strict, depend: BE_USE_STRICT_MODULE) { 34 | init, func(m_init) 35 | } 36 | @const_object_info_end */ 37 | #include "../generate/be_fixed_strict.h" 38 | #endif 39 | 40 | #endif /* BE_USE_STRICT_MODULE */ 41 | -------------------------------------------------------------------------------- /tests/json_advanced.be: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | 5 | 6 | def assert_load_failed(text) 7 | assert(json.load(text) == nil) 8 | end 9 | 10 | var input_file = open("tests/json_test_cases.json", "r") 11 | var test_cases = json.load(input_file.read()) 12 | 13 | # check positive cases 14 | var has_failed_positives = false 15 | for case_name : test_cases["positive"].keys() 16 | var case = test_cases["positive"][case_name] 17 | var val = json.load(case) 18 | if val == nil && case != "null" 19 | print("Failed to load case: " + case_name) 20 | has_failed_positives = true 21 | end 22 | end 23 | 24 | if has_failed_positives 25 | assert(false) 26 | end 27 | 28 | # check negative cases 29 | 30 | var has_failed_negatives = false 31 | for case_name : test_cases["negative"].keys() 32 | var case = test_cases["negative"][case_name] 33 | 34 | var val = json.load(case) 35 | if val != nil 36 | print("Failed to fail case: " + case_name + ", got: " + json.dump(val)) 37 | has_failed_negatives = true 38 | end 39 | end 40 | 41 | if has_failed_negatives 42 | # assert(false) 43 | end 44 | 45 | # check "any" cases, only for crashes 46 | 47 | for case_name : test_cases["any"].keys() 48 | var case = test_cases["any"][case_name] 49 | var val = json.load(case) 50 | end 51 | 52 | -------------------------------------------------------------------------------- /tests/super_leveled.be: -------------------------------------------------------------------------------- 1 | #- test for leveled use of super() -# 2 | 3 | #- setup -# 4 | class A def r() return 'a' end def f() return self.r() end end 5 | class B:A def r() return 'b' end def f() return super(self,A).f() + 'b' end end 6 | class C:B def r() return 'c' end def f() return super(self,B).f() + 'c' end end 7 | a=A() 8 | b=B() 9 | c=C() 10 | 11 | #- regular behavior -# 12 | assert(classname(a) == 'A') 13 | assert(classname(b) == 'B') 14 | assert(classname(c) == 'C') 15 | assert(a.r() == 'a') 16 | assert(b.r() == 'b') 17 | assert(c.r() == 'c') 18 | assert(a.f() == 'a') 19 | 20 | #- standard use of super() -# 21 | assert(super(a) == nil) 22 | assert(super(A) == nil) 23 | assert(classname(super(B)) == 'A') 24 | assert(classname(super(C)) == 'B') 25 | assert(classname(super(super(C))) == 'A') 26 | assert(super(super(super(C))) == nil) 27 | 28 | #- super() levele -# 29 | assert(super(a,A) == nil) 30 | assert(super(b,B) == nil) 31 | assert(super(c,C) == nil) 32 | assert(classname(super(c,B)) == 'B') 33 | assert(classname(super(c,A)) == 'A') 34 | assert(super(c,map) == nil) #- not a parent class -# 35 | 36 | #- wrapping it all -# 37 | assert(a.f() == 'a') 38 | assert(b.f() == 'bb') 39 | 40 | #- the last one is tricky: 41 | c.f() -> calls f() in class B -> calls f() in class A -> calls r() of overall class hence C 42 | -# 43 | assert(c.f() == 'cbc') -------------------------------------------------------------------------------- /tests/virtual_methods2.be: -------------------------------------------------------------------------------- 1 | #- virtual attributes -# 2 | 3 | def assert_attribute_error(f) 4 | try 5 | f() 6 | assert(false, 'unexpected execution flow') 7 | except .. as e, m 8 | assert(e == 'attribute_error') 9 | end 10 | end 11 | 12 | class Ta 13 | var a, b, virtual_c 14 | def init() 15 | self.a = 1 16 | self.b = 2 17 | self.virtual_c = 3 18 | end 19 | def member(n) 20 | if n == 'c' return self.virtual_c end 21 | return nil 22 | end 23 | def setmember(n, v) 24 | if n == 'c' self.virtual_c = v return true end 25 | return false 26 | end 27 | end 28 | ta = Ta() 29 | 30 | assert(ta.a == 1) 31 | assert(ta.b == 2) 32 | assert(ta.c == 3) 33 | ta.a = 10 34 | assert(ta.a == 10) 35 | assert(ta.c == 3) 36 | ta.c = 30 37 | assert(ta.c == 30) 38 | assert(ta.virtual_c == 30) 39 | assert_attribute_error(def() ta.d = 0 end) 40 | 41 | # bug: if a function is sent to 'setmember()', the internal BE_STATIC flag is added 42 | # which makes the function not callable and scrambles tostring() 43 | class A 44 | var _v 45 | def init() 46 | self._v = {} 47 | end 48 | def setmember(name, value) 49 | self._v[name] = value 50 | end 51 | end 52 | a = A() 53 | f = def(name, time_ms) return 42 end 54 | a.f = f 55 | 56 | assert(a._v['f']() == 42) 57 | -------------------------------------------------------------------------------- /tests/module.be: -------------------------------------------------------------------------------- 1 | # test for monkey patching of modules 2 | 3 | import string 4 | import introspect 5 | 6 | var string_orig = string 7 | 8 | introspect.setmodule("string", 42) 9 | import string 10 | assert(string == 42) 11 | 12 | # set back original value 13 | introspect.setmodule("string", string_orig) 14 | import string 15 | assert(type(string) == 'module') 16 | assert(str(string) == '') 17 | 18 | # 19 | # demo, how to monkey patch string with a new function `foo()` returning `bar` 20 | # 21 | import string 22 | import introspect 23 | var string_orig = string # keep a copy of the original string module 24 | var string_alt = module('string') # create a new module 25 | 26 | # function `super()` is a closure to capture the original value of string 27 | string_alt.super = def() 28 | return string_orig 29 | end 30 | 31 | # function `member()` is a clousre to capture the original value of string module 32 | string_alt.member = def(k) 33 | import introspect 34 | return introspect.get(string_orig, k, true) 35 | end 36 | 37 | string_alt.foo = def() return 'bar' end 38 | 39 | # replace the entry for module "string", from now on each `import string` will use `string_alt` 40 | introspect.setmodule("string", string_alt) 41 | import string 42 | 43 | # test the new string module 44 | assert(string.tolower('abCD') == 'abcd') 45 | assert(string.foo() == 'bar') 46 | -------------------------------------------------------------------------------- /tools/coc/bytes_build.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | class bytes_build: 4 | def __init__(self, map): 5 | self.map = map.copy() 6 | 7 | def build(self, path): 8 | prefix = path + "/be_const_bytes" 9 | self.writefile(prefix + "_def.h", self.build_bytes_def()) 10 | self.writefile(prefix + ".h", self.build_bytes_ext()) 11 | 12 | def writefile(self, filename, text): 13 | buf = "" 14 | try: 15 | with open(filename) as f: 16 | buf = f.read() 17 | except FileNotFoundError: 18 | pass 19 | if buf != text: 20 | with open(filename, "w") as f: 21 | f.write(text) 22 | 23 | def build_bytes_def(self): 24 | ostr = "" 25 | ostr += "/* binary arrays */\n" 26 | ostr += "be_define_const_bytes_empty();\n" 27 | for k in self.map: 28 | ostr += "be_define_const_bytes(" 29 | ostr += k + ", " + ", ".join( [ "0x" + k[i:i+2] for i in range(0, len(k), 2)] ) 30 | ostr += ");\n" 31 | 32 | return ostr 33 | 34 | def build_bytes_ext(self): 35 | ostr = "" 36 | ostr += "/* extern binary arrays */\n" 37 | ostr += "extern const binstance_arg3 be_const_instance_;\n" 38 | for k in self.map: 39 | ostr += "extern const binstance_arg3 be_const_instance_" + k + ";\n" 40 | 41 | return ostr -------------------------------------------------------------------------------- /examples/bintree.be: -------------------------------------------------------------------------------- 1 | # Reference from https://github.com/BerryMathDevelopmentTeam/BerryMath/blob/master/testscript/BinaryTree.bm 2 | 3 | class node 4 | var v, l, r 5 | def init(v, l, r) 6 | self.v = v 7 | self.l = l 8 | self.r = r 9 | end 10 | def insert(v) 11 | if v < self.v 12 | if self.l 13 | self.l.insert(v) 14 | else 15 | self.l = node(v) 16 | end 17 | else 18 | if self.r 19 | self.r.insert(v) 20 | else 21 | self.r = node (v) 22 | end 23 | end 24 | end 25 | def sort(l) 26 | if (self.l) self.l.sort(l) end 27 | l.push(self.v) 28 | if (self.r) self.r.sort(l) end 29 | end 30 | end 31 | 32 | class btree 33 | var root 34 | def insert(v) 35 | if self.root 36 | self.root.insert(v) 37 | else 38 | self.root = node(v) 39 | end 40 | end 41 | def sort() 42 | var l = [] 43 | if self.root 44 | self.root.sort(l) 45 | end 46 | return l 47 | end 48 | end 49 | 50 | var tree = btree() 51 | tree.insert(-100) 52 | tree.insert(5); 53 | tree.insert(3); 54 | tree.insert(9); 55 | tree.insert(10); 56 | tree.insert(10000000); 57 | tree.insert(1); 58 | tree.insert(-1); 59 | tree.insert(-10); 60 | print(tree.sort()); 61 | -------------------------------------------------------------------------------- /src/be_gclib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | #include "be_gc.h" 10 | 11 | #if BE_USE_GC_MODULE 12 | 13 | static int m_allocated(bvm *vm) 14 | { 15 | size_t count = be_gc_memcount(vm); 16 | if (count < 0x80000000) { 17 | be_pushint(vm, (bint)count); 18 | } else { 19 | be_pushreal(vm, (breal)count); 20 | } 21 | be_return(vm); 22 | } 23 | 24 | static int m_collect(bvm *vm) 25 | { 26 | be_gc_collect(vm); 27 | be_return_nil(vm); 28 | } 29 | 30 | #if !BE_USE_PRECOMPILED_OBJECT 31 | be_native_module_attr_table(gc){ 32 | be_native_module_function("allocated", m_allocated), 33 | be_native_module_function("collect", m_collect) 34 | }; 35 | 36 | be_define_native_module(gc, NULL); 37 | #else 38 | /* @const_object_info_begin 39 | module gc (scope: global, depend: BE_USE_GC_MODULE) { 40 | allocated, func(m_allocated) 41 | collect, func(m_collect) 42 | } 43 | @const_object_info_end */ 44 | #include "../generate/be_fixed_gc.h" 45 | #endif 46 | 47 | #endif /* BE_USE_SYS_MODULE */ 48 | -------------------------------------------------------------------------------- /tools/coc/coc_string.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def hashcode(s): 4 | """Compute hash for a string, in uint32_t""" 5 | hash = 2166136261 6 | for c in s.encode('utf8'): 7 | hash = ((hash ^ c) * 16777619) & 0xFFFFFFFF 8 | return hash 9 | 10 | def escape_operator_v1(s): 11 | tab = { 12 | # "void": "", 13 | "..": "opt_connect", 14 | "+": "opt_add", "-": "opt_sub", 15 | "*": "opt_mul", "/": "opt_div", 16 | "%": "opt_mod", "&": "opt_and", 17 | "^": "opt_xor", "|": "opt_or", 18 | "<": "opt_lt", ">": "opt_gt", 19 | "<=": "opt_le", ">=": "opt_ge", 20 | "==": "opt_eq", "!=": "opt_neq", 21 | "<<": "opt_shl", ">>": "opt_shr", 22 | "-*": "opt_neg", "~": "opt_flip", 23 | "()": "opt_call", 24 | } 25 | if s in tab: return tab[s] 26 | s2 = "" 27 | for c in s: 28 | if c == '.': s2 += 'dot_' 29 | else: s2 += c 30 | return s2 31 | 32 | def escape_operator(s): 33 | s = re.sub('_X', '_X_', s) # replace any esape sequence '_X' with '_XX' 34 | s = re.sub('[^a-zA-Z0-9_]', lambda m: "_X{0:02X}".format(ord(m.group())), s) 35 | return s 36 | 37 | def unescape_operator(s): 38 | s = re.sub('_X[0-9A-F][0-9A-F]', lambda m: chr(int(m.group()[2:], 16)), s) 39 | s = re.sub('_X_', '_X', s) 40 | return s 41 | -------------------------------------------------------------------------------- /tools/plugins/vscode/skiars.berry-1.2.0/.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Embedded Script 6 | none 7 | berry,Embedded Script,__ext_.berry 8 | Languages 9 | Public 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/class.be: -------------------------------------------------------------------------------- 1 | class Test 2 | var maximum 3 | def init(maximum) 4 | self.maximum = maximum 5 | end 6 | def iter() # method closure upvalues test 7 | var i = -1, maximum = self.maximum 8 | return def () 9 | i += 1 10 | if i > maximum 11 | raise 'stop_iteration' 12 | end 13 | return i 14 | end 15 | end 16 | end 17 | 18 | var sum = 0 19 | for i : Test(10) 20 | sum += i 21 | end 22 | assert(sum == 55, 'iteraion sum is ' + str(sum) + ' (expected 55).') 23 | 24 | #- test case for class instanciated from module member #103 -# 25 | 26 | m = module() 27 | g_i = 0 #- detect side effect from init() -# 28 | class C def init() g_i += 1 end end 29 | m.C = C 30 | 31 | #- normal invocation -# 32 | assert(type(C()) == 'instance') 33 | assert(g_i == 1) 34 | 35 | #- invoke from module member -# 36 | assert(type(m.C()) == 'instance') 37 | assert(g_i == 2) 38 | 39 | class C2 var C1 def init(c) self.C1 = c end end 40 | m.C2 = C2 41 | c2 = m.C2(m.C) 42 | 43 | assert(c2.C1 == C) 44 | 45 | c3 = m.C2(m.C()) 46 | assert(type(c3.C1) == 'instance') 47 | assert(classname(c3.C1) == 'C') 48 | 49 | #- an instance member can be a class and called directly -# 50 | class Test_class 51 | var c 52 | def init() 53 | self.c = map 54 | end 55 | end 56 | c4 = Test_class() 57 | assert(type(c4.c) == 'class') 58 | c5 = c4.c() 59 | assert(type(c5) == 'instance') 60 | assert(classname(c5) == 'map') -------------------------------------------------------------------------------- /src/be_mem.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_MEM_H 9 | #define BE_MEM_H 10 | 11 | #include "berry.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #define be_malloc(vm, size) be_realloc((vm), NULL, 0, (size)) 18 | #define be_free(vm, ptr, size) be_realloc((vm), (ptr), (size), 0) 19 | 20 | BERRY_API void* be_os_malloc(size_t size); 21 | BERRY_API void be_os_free(void *ptr); 22 | BERRY_API void* be_os_realloc(void *ptr, size_t size); 23 | BERRY_API void* be_realloc(bvm *vm, void *ptr, size_t old_size, size_t new_size); 24 | BERRY_API void be_gc_memory_pools(bvm *vm); 25 | BERRY_API void be_gc_free_memory_pools(bvm *vm); 26 | BERRY_API void be_gc_init_memory_pools(bvm *vm); 27 | BERRY_API void be_gc_memory_pools_info(bvm *vm, size_t* slots_used, size_t* slots_allocated); 28 | 29 | /* The following moves a portion of memory to constraint regions with 32-bits read/write acess */ 30 | /* Effective only if `BE_USE_MEM_ALIGNED` is set to `1`*/ 31 | BERRY_API void* be_move_to_aligned(bvm *vm, void *ptr, size_t size); 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(Berry C) 4 | set(CMAKE_C_STANDARD 99) 5 | 6 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 7 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 8 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 9 | 10 | set(BERRY_COC ${CMAKE_CURRENT_SOURCE_DIR}/tools/coc/coc) 11 | set(BERRY_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 12 | set(BERRY_CONFIG_DIR default CACHE FILEPATH "The directory of berry_conf.h.") 13 | set(BERRY_CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/${BERRY_CONFIG_DIR}/berry_conf.h) 14 | set(BERRY_GENERATE ${CMAKE_CURRENT_SOURCE_DIR}/generate) 15 | 16 | if (${CMAKE_HOST_WIN32}) 17 | set(BERRY_COC python ${BERRY_COC}) 18 | endif () 19 | 20 | file(MAKE_DIRECTORY generate) 21 | 22 | if(NOT WIN32) 23 | find_library(MATH_LIBRARY m) 24 | else() 25 | set(MATH_LIBRARY "") 26 | endif() 27 | 28 | # berry library 29 | file(GLOB SOURCES src/*.c) 30 | add_library(libberry ${SOURCES}) 31 | target_include_directories(libberry PUBLIC src ${BERRY_CONFIG_DIR}) 32 | 33 | add_custom_target(berry-coc 34 | COMMAND ${BERRY_COC} -o ${BERRY_GENERATE} ${BERRY_SOURCE_DIR} -c ${BERRY_CONFIG} 35 | DEPENDS ${SOURCES} COMMAND_EXPAND_LISTS 36 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 37 | COMMENT "Generate coc objects" 38 | ) 39 | add_dependencies(libberry berry-coc) 40 | 41 | # berry default exe 42 | file(GLOB SOURCES_EXE default/*.c) 43 | add_executable(berry ${SOURCES_EXE}) 44 | target_link_libraries(berry PUBLIC libberry ${MATH_LIBRARY}) 45 | -------------------------------------------------------------------------------- /tests/member_indirect.be: -------------------------------------------------------------------------------- 1 | #- new syntax for indirect members -# 2 | 3 | #- module accessor -# 4 | s_pi = 'pi' 5 | 6 | import math 7 | assert(math.('pi') == math.pi) 8 | assert(math.(s_pi) == math.pi) 9 | 10 | #- module writer -# 11 | m = module("m") 12 | 13 | m.('aa') = 1 14 | m.('a' + 'b') = 2 15 | s_ac = 'ac' 16 | m.(s_ac) = 3 17 | assert(m.aa == 1) 18 | assert(m.ab == 2) 19 | assert(m.ac == 3) 20 | assert(m.('a'+'a') == 1) 21 | 22 | #- class accessor -# 23 | class A1 24 | static a = 1, b = 2 25 | static s = "foo" 26 | def f() return 0 end 27 | end 28 | assert(A1.a == 1) 29 | assert(A1.b == 2) 30 | assert(A1.s == "foo") 31 | assert(type(A1.f) == 'function') 32 | 33 | #- instance accessor -# 34 | class A2 35 | var a, b 36 | static s_a = 'a' 37 | def init(a,b) 38 | self.(self.('s_a')) = a 39 | self.('b') = b 40 | end 41 | def f(x) 42 | return x+1 43 | end 44 | def g(a,b) 45 | return A2(a,b) 46 | end 47 | end 48 | a = A2(1,2) 49 | 50 | #- reading members -# 51 | assert(a.a == 1) 52 | assert(a.b == 2) 53 | assert(a.(A2.s_a) == 1) 54 | assert(a.('b') == 2) 55 | 56 | #- writing members -# 57 | a.('a') = 10 58 | a.('bb'[0]) = 11 59 | assert(a.a == 10) 60 | assert(a.b == 11) 61 | 62 | #- calling methods -# 63 | assert(a.f(1) == 2) 64 | assert(a.('f')(2) == 3) 65 | 66 | #- mulit-level -# 67 | assert(a.('g')(3,4).('a') == 3) 68 | a.('a') = a.g(3,4) 69 | assert(a.a.b == 4) 70 | assert(a.('a').b == 4) 71 | assert(a.('a').('b') == 4) 72 | assert(a.a.('b') == 4) 73 | 74 | a.('a').('b') += 1 75 | assert(a.a.b == 5) 76 | -------------------------------------------------------------------------------- /examples/repl.be: -------------------------------------------------------------------------------- 1 | do 2 | def ismult(msg) 3 | import string 4 | return string.split(msg, -5)[1] == '\'EOS\'' 5 | end 6 | 7 | def multline(src, msg) 8 | if !ismult(msg) 9 | print('syntax_error: ' + msg) 10 | return 11 | end 12 | while true 13 | try 14 | src += '\n' + input('>> ') 15 | return compile(src) 16 | except 'syntax_error' as e, m 17 | if !ismult(m) 18 | print('syntax_error: ' + m) 19 | return 20 | end 21 | end 22 | end 23 | end 24 | 25 | def parse() 26 | var fun, src = input('> ') 27 | try 28 | fun = compile('return (' + src + ')') 29 | except 'syntax_error' as e, m 30 | try 31 | fun = compile(src) 32 | except 'syntax_error' as e, m 33 | fun = multline(src, m) 34 | end 35 | end 36 | return fun 37 | end 38 | 39 | def run(fun) 40 | try 41 | var res = fun() 42 | if res print(res) end 43 | except .. as e, m 44 | import debug 45 | print(e .. ': ' .. m) 46 | debug.traceback() 47 | end 48 | end 49 | 50 | def repl() 51 | while true 52 | var fun = parse() 53 | if fun != nil 54 | run(fun) 55 | end 56 | end 57 | end 58 | 59 | print("Berry Berry REPL!") 60 | repl() 61 | end 62 | -------------------------------------------------------------------------------- /tests/bool.be: -------------------------------------------------------------------------------- 1 | # test cases for boolean expressions 2 | 3 | assert(1 != false && 1 != true) 4 | assert(0 != false && 0 != true) 5 | assert(!!1 == true) 6 | assert(!!0 == false) 7 | 8 | a = true 9 | b = false 10 | assert(!!list == true) 11 | assert(a && b == false) 12 | assert(!(a && b)) 13 | def test(a, b) 14 | while !(a && b) 15 | assert(false) 16 | end 17 | end 18 | test(true, true) 19 | 20 | # bug in unary 21 | def f(i) 22 | var j = !i # bug if i is erroneously modified 23 | return i 24 | end 25 | assert(f(1) == 1) 26 | 27 | #- addind bool() function -# 28 | assert(bool() == false) 29 | assert(bool(0) == false) 30 | assert(bool(0.0) == false) 31 | assert(bool(false) == false) 32 | assert(bool(nil) == false) 33 | 34 | assert(bool(-1) == true) 35 | assert(bool(3.5) == true) 36 | assert(bool('') == false) # changed behavior 37 | assert(bool('a') == true) 38 | assert(bool(list) == true) 39 | assert(bool(list()) == false) # changed behavior 40 | assert(bool([]) == false) # changed behavior 41 | assert(bool([0]) == true) 42 | assert(bool(map()) == false) # changed behavior 43 | assert(bool({}) == false) # changed behavior 44 | assert(bool({false:false}) == true) 45 | assert(bool({nil:nil}) == false)# changed behavior - `nil` key is ignored so the map is empty 46 | 47 | import introspect 48 | assert(bool(introspect.toptr(0x1000)) == true) 49 | assert(bool(introspect.toptr(0)) == false) 50 | 51 | # reproduce bug https://github.com/berry-lang/berry/issues/372 52 | def f() var a = false var b = true || a return a end 53 | assert(f() == false) 54 | -------------------------------------------------------------------------------- /tests/global.be: -------------------------------------------------------------------------------- 1 | #- test module global -# 2 | 3 | def assert_syntax_error(code) 4 | try 5 | var f = compile(code) 6 | assert(false, 'unexpected execution flow') 7 | except .. as e, m 8 | assert(e == 'syntax_error') 9 | end 10 | end 11 | def findinlist(l, e) 12 | for i: 0..size(l)-1 13 | if l[i] == e return i end 14 | end 15 | return nil 16 | end 17 | 18 | #- set the scene -# 19 | global_a = 1 20 | global_b = "bb" 21 | assert(global_a == 1) 22 | assert(global_b == "bb") 23 | 24 | assert_syntax_error("c") #- compilation fails because c does not exist -# 25 | 26 | import global 27 | 28 | assert(global.global_a == 1) 29 | assert(global.global_b == "bb") 30 | 31 | global.global_c = 3 32 | #- now compilation against 'c' global -# 33 | f = compile("return global_c") 34 | assert(f() == 3) 35 | 36 | #- check that access to non-existent global returns nil (new behavior) -# 37 | assert(global.d == nil) 38 | 39 | #- check the glbal list -# 40 | assert(findinlist(global(), 'global_a') != nil) 41 | assert(findinlist(global(), 'global_b') != nil) 42 | assert(findinlist(global(), 'global_c') != nil) 43 | assert(findinlist(global(), 'global_d') == nil) 44 | 45 | # undef 46 | var a_global_var = 1 47 | assert(global.contains("a_global_var") == true) 48 | assert(global().find("a_global_var") != nil) 49 | 50 | global.undef("a_global_var") 51 | assert(global.contains("a_global_var") == false) 52 | assert(global().find("a_global_var") == nil) 53 | 54 | global.a_global_var = 1 55 | assert(global.contains("a_global_var") == true) 56 | global.undef("a_global_var") 57 | -------------------------------------------------------------------------------- /examples/qsort.be: -------------------------------------------------------------------------------- 1 | def qsort(data) 2 | # do once sort 3 | def once(left, right) 4 | var pivot = data[left] # use the 0th value as the pivot 5 | while left < right # check if sort is complete 6 | # put the value less than the pivot to the left 7 | while left < right && data[right] >= pivot 8 | right -= 1 # skip values greater than pivot 9 | end 10 | data[left] = data[right] 11 | # put the value greater than the pivot on the right 12 | while left < right && data[left] <= pivot 13 | left += 1 # skip values less than pivot 14 | end 15 | data[right] = data[left] 16 | end 17 | # now we have the index of the pivot, store it 18 | data[left] = pivot 19 | return left # return the index of the pivot 20 | end 21 | # recursive quick sort algorithm 22 | def _sort(left, right) 23 | if left < right # executed when the array is not empty 24 | var index = once(left, right) # get index of pivot for divide and conquer 25 | _sort(left, index - 1) # sort the data on the left 26 | _sort(index + 1, right) # sort the data on the right 27 | end 28 | end 29 | # start quick sort 30 | _sort(0, data.size() - 1) 31 | return data 32 | end 33 | 34 | import time, math 35 | math.srand(time.time()) # sse system time as a random seed 36 | data = [] 37 | # put 20 random numbers into the array 38 | for i : 1 .. 20 39 | data.push(math.rand() % 100) 40 | end 41 | # sort and print 42 | print(qsort(data)) 43 | -------------------------------------------------------------------------------- /src/be_sys.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_SYS_H 9 | #define BE_SYS_H 10 | 11 | #include 12 | 13 | /* directory information for directory traversal */ 14 | typedef struct { 15 | void *dir; 16 | void *file; 17 | const char *name; 18 | } bdirinfo; 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | void* be_fopen(const char *filename, const char *modes); 25 | int be_fclose(void *hfile); 26 | size_t be_fwrite(void *hfile, const void *buffer, size_t length); 27 | size_t be_fread(void *hfile, void *buffer, size_t length); 28 | char* be_fgets(void *hfile, void *buffer, int size); 29 | int be_fseek(void *hfile, long offset); 30 | long int be_ftell(void *hfile); 31 | long int be_fflush(void *hfile); 32 | size_t be_fsize(void *hfile); 33 | int be_isdir(const char *path); 34 | int be_isfile(const char *path); 35 | int be_isexist(const char *path); 36 | char* be_getcwd(char *buf, size_t size); 37 | int be_chdir(const char *path); 38 | int be_mkdir(const char *path); 39 | int be_unlink(const char *filename); 40 | int be_dirfirst(bdirinfo *info, const char *path); 41 | int be_dirnext(bdirinfo *info); 42 | int be_dirclose(bdirinfo *info); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /src/be_module.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_MODULE_H 9 | #define BE_MODULE_H 10 | 11 | #include "be_object.h" 12 | 13 | #define BE_MODULE_NAME 1 14 | 15 | typedef struct bmodule { 16 | bcommon_header; 17 | bmap *table; 18 | union infodata { 19 | const bntvmodule_t *native; 20 | const char *name; 21 | const bstring *sname; 22 | #ifdef __cplusplus 23 | BE_CONSTEXPR infodata(const char *name) : name(name) {} 24 | #endif 25 | } info; 26 | bgcobject *gray; /* for gc gray list */ 27 | #ifdef __cplusplus 28 | BE_CONSTEXPR bmodule(bmap *tab, const char *name) : 29 | next(0), type(BE_MODULE), marked(GC_CONST), 30 | table(tab), info(infodata(name)), gray(0) {} 31 | #endif 32 | } bmodule; 33 | 34 | bmodule* be_module_new(bvm *vm); 35 | void be_module_delete(bvm *vm, bmodule *module); 36 | int be_module_load(bvm *vm, bstring *path); 37 | void be_cache_module(bvm *vm, bstring *name); 38 | int be_module_attr(bvm *vm, bmodule *module, bstring *attr, bvalue *dst); 39 | bbool be_module_setmember(bvm *vm, bmodule *module, bstring *attr, bvalue *src); 40 | const char* be_module_name(bmodule *module); 41 | bbool be_module_setname(bmodule *module, bstring *name); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/be_list.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_LIST_H 9 | #define BE_LIST_H 10 | 11 | #include "be_object.h" 12 | 13 | struct blist { 14 | bcommon_header; 15 | bgcobject *gray; /* for gc gray list */ 16 | int count, capacity; 17 | bvalue *data; 18 | }; 19 | 20 | #define be_list_data(list) ((list)->data) 21 | #define be_list_count(list) ((list)->count) 22 | #define be_list_at(list, index) ((list)->data + index) 23 | #define be_list_end(list) ((list)->data + (list)->count) 24 | 25 | blist* be_list_new(bvm *vm); 26 | void be_list_delete(bvm *vm, blist *list); 27 | blist* be_list_copy(bvm *vm, blist *original); 28 | bvalue* be_list_index(blist *list, int index); 29 | bvalue* be_list_push(bvm *vm, blist *list, bvalue *value); 30 | bvalue* be_list_insert(bvm *vm, blist *list, int index, bvalue *value); 31 | int be_list_remove(bvm *vm, blist *list, int index); 32 | void be_list_resize(bvm *vm, blist *list, int count); 33 | void be_list_merge(bvm *vm, blist *list, const blist *other); 34 | void be_list_reverse(blist *list); 35 | void be_list_pool_init(bvm *vm, blist *list); 36 | int be_list_pool_alloc(bvm *vm, blist *list, bvalue *src); 37 | void be_list_pool_free(blist *list, int id); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /tests/virtual_methods.be: -------------------------------------------------------------------------------- 1 | #- basic initialization -# 2 | 3 | def assert_attribute_error(f) 4 | try 5 | f() 6 | assert(false, 'unexpected execution flow') 7 | except .. as e, m 8 | assert(e == 'attribute_error') 9 | end 10 | end 11 | 12 | class T1 13 | var a, b 14 | def init() 15 | self.a = 1 16 | self.b = 2 17 | end 18 | def f() return true end 19 | def g() return false end 20 | end 21 | t = T1() 22 | 23 | #- warm up -# 24 | assert(t.a == 1) 25 | assert(t.b == 2) 26 | assert(t.f() == true) 27 | assert(t.g() == false) 28 | 29 | #- test normal errors when method does not exist -# 30 | assert_attribute_error(/-> t.h()) 31 | assert_attribute_error(/-> t.c) 32 | 33 | class T2 : T1 34 | def member(n) 35 | import undefined 36 | if (n == 'f1') return / n -> n end 37 | if (n == 'f2') return /-> 4 end 38 | if (n == 'a1') return 10 end 39 | if (n == 'h') return undefined end 40 | end 41 | end 42 | t2 = T2() 43 | 44 | #- test non-regression -# 45 | assert(t2.a == 1) 46 | assert(t2.b == 2) 47 | assert(t2.f() == true) 48 | assert(t2.g() == false) 49 | assert_attribute_error(/-> t2.h()) 50 | 51 | #- try virtual methods -# 52 | assert(t2.f1() == t2) 53 | assert(t2.f2() == 4) 54 | assert(t2.a1 == 10) 55 | assert(t2.foo == nil) 56 | 57 | #- module -# 58 | m = module("m") 59 | 60 | m.a = 1 61 | assert(m.a == 1) 62 | assert_attribute_error(/-> m.b) 63 | 64 | m.member = def(n) 65 | import undefined 66 | if n == "b" return 2 end 67 | if n == "c" return undefined end 68 | end 69 | 70 | assert(m.b == 2) 71 | assert_attribute_error(/-> m.c) 72 | assert(m.d == nil) #- returns nil if no response -# 73 | -------------------------------------------------------------------------------- /tests/os.be: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # os.path.join test 4 | assert(os.path.join('') == '') 5 | assert(os.path.join('abc', 'de') == 'abc/de') 6 | assert(os.path.join('abc', '/de') == '/de') 7 | assert(os.path.join('a', 'de') == 'a/de') 8 | assert(os.path.join('abc/', 'de') == 'abc/de') 9 | assert(os.path.join('abc', 'de', '') == 'abc/de/') 10 | assert(os.path.join('abc', '', '', 'de') == 'abc/de') 11 | assert(os.path.join('abc', '/de', 'fghij') == '/de/fghij') 12 | assert(os.path.join('abc', 'xyz', '/de', 'fghij') == '/de/fghij') 13 | 14 | # os.path.split test 15 | def split(st, lst) 16 | var res = os.path.split(st) 17 | assert(res[0] == lst[0] && res[1] == lst[1], 18 | 'unexpected results: ' .. res .. ', reference value: ' .. lst) 19 | end 20 | 21 | split('/', ['/', '']) 22 | split('//', ['//', '']) 23 | split('///', ['///', '']) 24 | split('a/', ['a', '']) 25 | split('a//', ['a', '']) 26 | split('a/b/c', ['a/b', 'c']) 27 | split('a/b/', ['a/b', '']) 28 | split('a//b//', ['a//b', '']) 29 | split('a/../b', ['a/..', 'b']) 30 | split('abcd////ef/////', ['abcd////ef', '']) 31 | split('abcd////ef', ['abcd', 'ef']) 32 | 33 | # os.path.splitext test 34 | def splitext(st, lst) 35 | var res = os.path.splitext(st) 36 | assert(res[0] == lst[0] && res[1] == lst[1], 37 | 'unexpected results: ' .. res .. ', reference value: ' .. lst) 38 | end 39 | 40 | splitext('a.b', ['a', '.b']) 41 | splitext('a..b', ['a.', '.b']) 42 | splitext('/a..b', ['/a.', '.b']) 43 | splitext('/.b', ['/.b', '']) 44 | splitext('/..b', ['/..b', '']) 45 | splitext('..b', ['..b', '']) 46 | splitext('...b', ['...b', '']) 47 | splitext('.b', ['.b', '']) 48 | splitext('ac..b', ['ac.', '.b']) 49 | splitext('ac.b', ['ac', '.b']) 50 | splitext('ac/.b', ['ac/.b', '']) 51 | splitext('ac/..b', ['ac/..b', '']) 52 | -------------------------------------------------------------------------------- /tools/coc/macro_table.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | def int_safe(v): 4 | try: return int(v, 0) 5 | except: return 0 6 | 7 | class macro_table: 8 | pat = re.compile("(?:\\n|$)\\s*#define\\s+(\\w+)[ \\t]+(\\w*)", re.MULTILINE) 9 | pat_query = re.compile("(!?)(\\w+)") 10 | 11 | def __init__(self): 12 | self.map = {} 13 | 14 | # def readfile(self, filename): 15 | # with open(filename) as f: 16 | # return f.read() 17 | 18 | def parse_value(self, s): 19 | if len(s) == 0: return 1 # defined a macro name but no content, considered true 20 | if not s[0].isnumeric(): return 1 21 | return int_safe(s) 22 | 23 | def scan_file(self, filename): 24 | str = "" 25 | with open(filename, encoding='utf-8') as f: 26 | str = f.read() 27 | r = macro_table.pat.findall(str) 28 | for it in r: 29 | # print(f"> it0:{it[0]} it1:{it[1]}") 30 | self.map[it[0]] = self.parse_value(it[1]) 31 | 32 | def query(self, s): 33 | r = macro_table.pat_query.search(s) 34 | value = False 35 | if r: 36 | bang = r[1] 37 | name = r[2] 38 | # print(f">query: bang={bang} name={name} value={self.map.get(name)}") 39 | if name in self.map: 40 | value = int(self.map[name]) != 0 41 | if bang == "!": 42 | value = not value 43 | # print(f">query: {s}:{value}") 44 | return value 45 | 46 | if __name__ == '__main__': 47 | # from hash_map import * 48 | pat = re.compile("(?:\\n|$)\\s*#define\\s+(\\w+)[ \\t]+(\\w+)", re.MULTILINE) 49 | s = "aaa\n#define A 1 // a \n #define B 2\n#define C \n#define D 0 \n #define E 11 \na" 50 | -------------------------------------------------------------------------------- /modules/binary_heap.be: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/Binary_heap 2 | # This allows to choose the M first elements of an N-sized array 3 | # with respect to a comparison predicate cmp that defines a total order. 4 | # This avoids the overhead of sorting the entire array and then picking 5 | # the first elements. This is related to a priority queue. 6 | # We also define a binary heap based sort() of an entire array. 7 | 8 | var binary_heap = module("binary_heap") 9 | 10 | binary_heap._heapify = def(array, cmp, i) 11 | var m = i, child, e, am, ac 12 | while true 13 | child = 2 * i + 1 14 | if child >= array.size() return end 15 | ac = array[child] 16 | am = array[m] 17 | if cmp(ac, am) m = child am = ac end 18 | child += 1 19 | if child < array.size() 20 | ac = array[child] 21 | if cmp(ac, am) m = child am = ac end 22 | end 23 | if m == i break end 24 | array[m] = array[i] 25 | array[i] = am 26 | i = m 27 | end 28 | end 29 | 30 | # similar to C++11 std::make_heap 31 | binary_heap.make_heap = def(array, cmp) 32 | var i = size(array) / 2 33 | while i >= 0 binary_heap._heapify(array, cmp, i) i -= 1 end 34 | end 35 | 36 | # similar to C++11 std::pop_heap, but removes and returns the element 37 | binary_heap.remove_heap = def(array, cmp) 38 | var m = array.size() 39 | if m < 2 return m == 1 ? array.pop() : nil end 40 | m = array[0] 41 | array[0] = array.pop() 42 | binary_heap._heapify(array, cmp, 0) 43 | return m 44 | end 45 | 46 | # https://en.wikipedia.org/wiki/Heapsort 47 | binary_heap.sort = def(array, cmp) 48 | var i = array.size(), heap = array.copy() 49 | binary_heap.make_heap(heap, cmp) 50 | array.clear() 51 | while i > 0 array.push(binary_heap.remove_heap(heap, cmp)) i -= 1 end 52 | end 53 | 54 | return binary_heap 55 | -------------------------------------------------------------------------------- /tests/lexer.be: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def check(a, b) 4 | assert(math.abs(a - b) < 1e-6) 5 | end 6 | 7 | def test_source(src, msg) 8 | try 9 | compile(src) 10 | assert(false, 'unexpected execution flow') 11 | except .. as e, m 12 | assert(e == 'syntax_error') 13 | assert(m == 'string:1: ' + msg) 14 | end 15 | end 16 | 17 | #---- 18 | this is a 19 | mult-line comment 20 | ----# 21 | 22 | compile('x = 5; 0..x') 23 | assert('\x5a' == 'Z') 24 | assert('\132' == 'Z') 25 | assert('\a\b\f\n\r\t\v\\\'\"\?' == '\x07\x08\x0c\x0a\x0d\x09\x0b\x5c\x27\x22\x3f') 26 | assert(.45 == 0.45) 27 | assert(0X10 == 16) 28 | assert(0x10 == 16) 29 | assert(0X1A == 26) 30 | assert(0x1a == 26) 31 | check(45., 45) 32 | check(45.e-1, 4.5) 33 | check(45.E-1, 4.5) 34 | check(45.1e-1, 4.51) 35 | check(45.1e2, 4510) 36 | check(45.e2, 4500) 37 | check(45.e+2, 4500) 38 | 39 | # Ensure pathologically long numbers don't crash the lexer (or cause an buffer overflow) 40 | assert(000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 == 0.0); 41 | 42 | 43 | test_source('x = 5; 0...x;', 'unexpected symbol near \'.\'') 44 | test_source('x = 5; 0...x;', 'unexpected symbol near \'.\'') 45 | test_source('0xg', 'invalid hexadecimal number') 46 | test_source('"\\x5g"', 'invalid hexadecimal number') 47 | test_source('0x5g', 'malformed number') 48 | test_source('"\\779"', 'invalid octal number') 49 | test_source('"\n', 'unfinished string') 50 | 51 | var malformed_numbers = [ 52 | '45f', 53 | '45.f', 54 | '45.ef', 55 | '45.e-f', 56 | '45.e-1f', 57 | '45.e-1.', 58 | '45.5.', 59 | '0x45.', 60 | '0x45j' 61 | ] 62 | 63 | for i : malformed_numbers 64 | test_source(i, 'malformed number') 65 | end 66 | -------------------------------------------------------------------------------- /src/be_string.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_STRING_H 9 | #define BE_STRING_H 10 | 11 | #include "be_object.h" 12 | 13 | #define SHORT_STR_MAX_LEN 64 14 | 15 | typedef struct { 16 | bstring_header; 17 | #if BE_USE_STR_HASH_CACHE 18 | uint32_t hash; 19 | #endif 20 | /* char s[]; */ 21 | } bsstring; 22 | 23 | typedef struct { 24 | bstring str; 25 | int llen; 26 | /* char s[]; */ 27 | } blstring; 28 | 29 | typedef struct { /* const long string */ 30 | bstring_header; 31 | int llen; 32 | char s[]; 33 | } bclstring; 34 | 35 | typedef struct { 36 | bstring_header; 37 | uint32_t hash; 38 | const char *s; 39 | } bcstring; 40 | 41 | #define str_len(_s) \ 42 | ((_s)->slen == 255 ? cast(blstring*, _s)->llen : (_s)->slen) 43 | 44 | #define str(_s) be_str2cstr(_s) 45 | #define str_extra(_s) ((_s)->extra) 46 | #define str_literal(_vm, _s) be_newstrn((_vm), (_s), sizeof(_s) - 1) 47 | 48 | #if BE_USE_PRECOMPILED_OBJECT 49 | #include "../generate/be_const_strtab.h" 50 | #endif 51 | 52 | void be_string_init(bvm *vm); 53 | void be_string_deleteall(bvm *vm); 54 | int be_eqstr(bstring *s1, bstring *s2); 55 | bstring* be_newstr(bvm *vm, const char *str); 56 | bstring* be_newstrn(bvm *vm, const char *str, size_t len); 57 | bstring* be_newlongstr(bvm *vm, const char *str, size_t len); 58 | void be_gcstrtab(bvm *vm); 59 | uint32_t be_strhash(const bstring *s); 60 | const char* be_str2cstr(const bstring *s); 61 | void be_str_setextra(bstring *s, int extra); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /tests/introspect.be: -------------------------------------------------------------------------------- 1 | #- introspect -# 2 | import introspect 3 | 4 | #- test for modules -# 5 | m = module("m") 6 | m.a = 1 7 | m.b = def () return "foo" end 8 | 9 | assert(introspect.members(m) == ['a', 'b']) 10 | assert(introspect.get(m, 'a') == 1) 11 | assert(type(introspect.get(m, 'b')) == 'function') 12 | 13 | introspect.set(m, 'a', 2) 14 | assert(m.a == 2) 15 | 16 | #- test for instance -# 17 | class A var a,b static c=1,d=2 def f() end end 18 | a=A() 19 | 20 | assert(introspect.members(A) == ['a', 'f', 'b', 'c', 'd']) #- class members -# 21 | assert(introspect.members(a) == ['a', 'f', 'b', 'c', 'd']) #- instance members -# 22 | 23 | assert(introspect.get(a, 'c') == 1) 24 | assert(introspect.get(a, 'd') == 2) 25 | assert(introspect.get(a, 'a') == nil) 26 | 27 | introspect.set(a, 'a', 3) 28 | assert(a.a == 3) 29 | 30 | #- load module dynamically -# 31 | import introspect 32 | m = introspect.module("math") # load module `math`, assign to `m` and don't create a global variable 33 | assert(type(m.pi) == 'real') 34 | 35 | #- name -# 36 | import string 37 | assert(introspect.name(string) == 'string') 38 | assert(introspect.name(print) == nil) # native C function don't have a registered name 39 | assert(introspect.name("foo") == nil) 40 | class A def a() end static def b() end static var c end 41 | assert(introspect.name(A) == 'A') 42 | assert(introspect.name(A.a) == 'a') 43 | assert(introspect.name(A.b) == 'b') 44 | assert(introspect.name(A.c) == nil) 45 | 46 | # test introspect get and set 47 | # class and instance 48 | class A 49 | static var a 50 | var b 51 | end 52 | 53 | a = A() 54 | introspect.set(A, "a", 10) 55 | assert(A.a == 10) 56 | assert(introspect.get(A, "a") == 10) 57 | 58 | introspect.set(a, "b", 20) 59 | assert(a.b == 20) 60 | assert(introspect.get(a, "b") == 20) 61 | 62 | # module 63 | m = module('m') 64 | introspect.set(m, 'c', 30) 65 | assert(m.c == 30) 66 | assert(introspect.get(m, 'c') == 30) 67 | -------------------------------------------------------------------------------- /tests/bytes_fixed.be: -------------------------------------------------------------------------------- 1 | #- test for bytes with fixed sizes -# 2 | def assert_attribute_error(f) 3 | try 4 | f() 5 | assert(false, 'unexpected execution flow') 6 | except .. as e, m 7 | assert(e == 'attribute_error') 8 | end 9 | end 10 | 11 | #- simple initialization -# 12 | b=bytes(-1) 13 | assert(str(b) == "bytes('00')") 14 | b=bytes(-5) 15 | assert(str(b) == "bytes('0000000000')") 16 | 17 | #- initialization with hex -# 18 | b=bytes("11", -1) 19 | assert(str(b) == "bytes('11')") 20 | b=bytes("11", -5) 21 | assert(str(b) == "bytes('1100000000')") 22 | b=bytes("11223344", -4) 23 | assert(str(b) == "bytes('11223344')") 24 | assert(str(bytes(-3).fromb64('RFVm')) == "bytes('445566')") 25 | 26 | #- check that get and set still works -# 27 | b=bytes("11223344",-4) 28 | assert(b.get(0,2) == 0x2211) 29 | b.set(0,0x5566,2) 30 | assert(b.get(0,2) == 0x5566) 31 | assert(b[0] == 0x66) 32 | b[0]=12 33 | assert(b[0] == 12) 34 | 35 | #- resize -# 36 | b=bytes("11223344",-4) 37 | assert(b.resize(4) == bytes('11223344')) 38 | 39 | #- check that changing size raises an exception -# 40 | b=bytes("112233", -3) 41 | assert_attribute_error(/-> b.add(1)) 42 | assert_attribute_error(/-> b.add(2,2)) 43 | assert_attribute_error(/-> b.resize(4)) 44 | assert_attribute_error(/-> b.fromstring("aaaaa")) 45 | assert_attribute_error(/-> b.fromb64('RFVmdw==')) 46 | assert_attribute_error(/-> b.clear()) 47 | assert_attribute_error(/-> b.bytes()) 48 | 49 | #- add -# 50 | b=bytes("112233", -3) 51 | assert(b+b == bytes("112233112233")) 52 | 53 | #- bytes mapped -# 54 | b1=bytes("112233445566") 55 | ptr=b1._buffer() 56 | b2=bytes(ptr, -4) 57 | assert(b2 == bytes("11223344")) 58 | b2=bytes(ptr, 4) 59 | assert(b2 == bytes("11223344")) 60 | b2=bytes(ptr, 6) 61 | assert(b2 == bytes("112233445566")) 62 | assert(b1 == b2) 63 | 64 | b2.set(0,0xAABB,2) 65 | assert(b2 == bytes("BBAA33445566")) 66 | assert(b1 == bytes("BBAA33445566")) 67 | assert(b1 == b2) 68 | assert(b1.ismapped() == false) -------------------------------------------------------------------------------- /src/be_map.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_MAP_H 9 | #define BE_MAP_H 10 | 11 | #include "be_object.h" 12 | 13 | typedef struct bmapkey { 14 | union bvaldata v; 15 | uint32_t type:8; 16 | uint32_t next:24; 17 | } bmapkey; 18 | 19 | typedef struct bmapnode { 20 | bmapkey key; 21 | bvalue value; 22 | } bmapnode; 23 | 24 | struct bmap { 25 | bcommon_header; 26 | bgcobject *gray; /* for gc gray list */ 27 | bmapnode *slots; 28 | bmapnode *lastfree; 29 | int size; 30 | int count; 31 | #ifdef __cplusplus 32 | BE_CONSTEXPR bmap(bmapnode *s, int n) : 33 | next(0), type(BE_MAP), marked(GC_CONST), gray(0), 34 | slots(s), lastfree(0), size(n), count(n) {} 35 | #endif 36 | }; 37 | 38 | typedef bmapnode *bmapiter; 39 | 40 | #define be_map_iter() NULL 41 | #define be_map_count(map) ((map)->count) 42 | #define be_map_size(map) (map->size) 43 | 44 | #define be_map_key2value(dst, node) do { \ 45 | (dst)->type = (node)->key.type; \ 46 | (dst)->v = (node)->key.v; \ 47 | } while (0); 48 | 49 | bmap* be_map_new(bvm *vm); 50 | void be_map_delete(bvm *vm, bmap *map); 51 | bvalue* be_map_find(bvm *vm, bmap *map, bvalue *key); 52 | bvalue* be_map_insert(bvm *vm, bmap *map, bvalue *key, bvalue *value); 53 | int be_map_remove(bvm *vm, bmap *map, bvalue *key); 54 | bvalue* be_map_findstr(bvm *vm, bmap *map, bstring *key); 55 | bvalue* be_map_insertstr(bvm *vm, bmap *map, bstring *key, bvalue *value); 56 | void be_map_removestr(bvm *vm, bmap *map, bstring *key); 57 | bmapnode* be_map_next(bmap *map, bmapiter *iter); 58 | bmapnode* be_map_val2node(bvalue *value); 59 | void be_map_compact(bvm *vm, bmap *map); 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /tests/compiler.be: -------------------------------------------------------------------------------- 1 | #- test for issue #117 -# 2 | 3 | class A var a end 4 | a=A() 5 | a.a = ["foo", "bar"] 6 | 7 | s = nil 8 | def fs(m) s = m end 9 | 10 | class B 11 | var b, i 12 | def nok() 13 | fs(self.b.a[self.i]) # wrong behavior 14 | end 15 | def ok() 16 | var i = self.i 17 | fs(self.b.a[i]) # works correctly 18 | end 19 | end 20 | b=B() 21 | b.i=0 22 | b.b=a 23 | 24 | b.nok() 25 | assert(s == "foo") 26 | 27 | b.ok() 28 | assert(s == "foo") 29 | 30 | # detect a wrong compilation when accessing index 31 | # Berry compilation problem: 32 | # 33 | # ```berry 34 | # def f(self) print(self.a[128]) end 35 | # ``` 36 | # 37 | # Compilation assigns unwanted registers: 38 | # ``` 39 | # 0x60040001, // 0000 GETGBL R1 G1 40 | # 0x540A007F, // 0001 LDINT R2 128 41 | # 0x880C0100, // 0002 GETMBR R3 R0 K0 42 | # 0x94080602, // 0003 GETIDX R2 R3 R2 43 | # 0x5C100400, // 0004 MOVE R4 R2 <- PROBLEM 44 | # 0x7C040200, // 0005 CALL R1 1 45 | # 0x80000000, // 0006 RET 0 46 | # ``` 47 | # 48 | # With the fix, the integer is retrieved in second place, and erroneous register is not allocated: 49 | # ``` 50 | # 0x60040001, // 0000 GETGBL R1 G1 51 | # 0x88080100, // 0001 GETMBR R2 R0 K0 52 | # 0x540E007F, // 0002 LDINT R3 128 53 | # 0x94080403, // 0003 GETIDX R2 R2 R3 54 | # 0x7C040200, // 0004 CALL R1 1 55 | # 0x80000000, // 0005 RET 0 56 | # ``` 57 | def f(a,b) return b end 58 | l = [1,2,3,4] 59 | assert(f(l[-1],l[-2]) == 3) 60 | 61 | # Compileation problem: 62 | # def test() 63 | # var line = '1234567890' 64 | # line = line[3..7] 65 | # # print(line) 66 | # for n : 1..2 end 67 | # end 68 | # test() 69 | 70 | # BRY: Exception> 'attribute_error' - the 'range' object has no method '()' 71 | # stack traceback: 72 | # :5: in function `test` 73 | # :7: in function `main` 74 | def test() 75 | var line = '1234567890' 76 | line = line[3..7] 77 | # print(line) 78 | for n : 1..2 end 79 | end 80 | test() 81 | -------------------------------------------------------------------------------- /src/be_exec.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_EXEC_H 9 | #define BE_EXEC_H 10 | 11 | #include "be_object.h" 12 | #include 13 | 14 | /* protected-call function */ 15 | typedef void (*bpfunc)(bvm *vm, void *data); 16 | 17 | #if BE_DEBUG 18 | bvalue* be_incrtop(bvm *vm); 19 | #else 20 | /* increase top register */ 21 | #define be_incrtop(vm) ((vm)->top++) 22 | #endif 23 | 24 | #define be_stackpop(vm, n) ((vm)->top -= (n)) 25 | 26 | /* in MinGW-w64, setjmp / longjmp may be broken, 27 | * so here is replaced by __builtin version. */ 28 | #if defined(__GNUC__) && defined(__MINGW32__) && !defined(__clang__) 29 | #define be_setjmp(env) __builtin_setjmp(env) 30 | #define be_longjmp(env, v) __builtin_longjmp(env, v) 31 | #else 32 | #define be_setjmp(env) setjmp(env) 33 | #define be_longjmp(env, v) longjmp(env, v) 34 | #endif 35 | 36 | typedef jmp_buf bjmpbuf; 37 | 38 | struct blongjmp { 39 | bjmpbuf b; 40 | struct blongjmp *prev; 41 | volatile int status; /* error code */ 42 | }; 43 | 44 | struct bexecptframe { 45 | struct blongjmp errjmp; /* long jump information */ 46 | int depth; /* function call stack depth */ 47 | binstruction *ip; /* instruction pointer */ 48 | int refcount; /* save object reference stack */ 49 | }; 50 | 51 | void be_throw(bvm *vm, int errorcode); 52 | int be_execprotected(bvm *vm, bpfunc f, void *data); 53 | int be_protectedparser(bvm *vm, const char *fname, 54 | breader reader, void *data, bbool islocal); 55 | int be_protectedcall(bvm *vm, bvalue *v, int argc); 56 | void be_stackpush(bvm *vm); 57 | void be_stack_expansion(bvm *vm, int n); 58 | void be_except_block_setup(bvm *vm); 59 | void be_except_block_resume(bvm *vm); 60 | void be_except_block_close(bvm *vm, int count); 61 | void be_save_stacktrace(bvm *vm); 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/be_timelib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "berry.h" 9 | #include 10 | 11 | #if BE_USE_TIME_MODULE 12 | 13 | static int m_time(bvm *vm) 14 | { 15 | be_pushint(vm, (bint)time(NULL)); 16 | be_return(vm); 17 | } 18 | 19 | static void time_insert(bvm *vm, const char *key, int value) 20 | { 21 | be_pushstring(vm, key); 22 | be_pushint(vm, value); 23 | be_data_insert(vm, -3); 24 | be_pop(vm, 2); 25 | } 26 | 27 | static int m_dump(bvm *vm) 28 | { 29 | if (be_top(vm) >= 1 && be_isint(vm, 1)) { 30 | time_t ts = be_toint(vm, 1); 31 | struct tm *t = localtime(&ts); 32 | be_newobject(vm, "map"); 33 | time_insert(vm, "year", t->tm_year + 1900); 34 | time_insert(vm, "month", t->tm_mon + 1); 35 | time_insert(vm, "day", t->tm_mday); 36 | time_insert(vm, "hour", t->tm_hour); 37 | time_insert(vm, "min", t->tm_min); 38 | time_insert(vm, "sec", t->tm_sec); 39 | time_insert(vm, "weekday", t->tm_wday); 40 | time_insert(vm, "epoch", ts); 41 | be_pop(vm, 1); 42 | be_return(vm); 43 | } 44 | be_return_nil(vm); 45 | } 46 | 47 | static int m_clock(bvm *vm) 48 | { 49 | be_pushreal(vm, clock() / (breal)CLOCKS_PER_SEC); 50 | be_return(vm); 51 | } 52 | 53 | #if !BE_USE_PRECOMPILED_OBJECT 54 | be_native_module_attr_table(time) { 55 | be_native_module_function("time", m_time), 56 | be_native_module_function("dump", m_dump), 57 | be_native_module_function("clock", m_clock) 58 | }; 59 | 60 | be_define_native_module(time, NULL); 61 | #else 62 | /* @const_object_info_begin 63 | module time (scope: global, depend: BE_USE_TIME_MODULE) { 64 | time, func(m_time) 65 | dump, func(m_dump) 66 | clock, func(m_clock) 67 | } 68 | @const_object_info_end */ 69 | #include "../generate/be_fixed_time.h" 70 | #endif 71 | 72 | #endif /* BE_USE_TIME_MODULE */ 73 | -------------------------------------------------------------------------------- /src/be_vector.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_VECTOR_H 9 | #define BE_VECTOR_H 10 | 11 | #include "be_object.h" 12 | 13 | /* =============================== defines =============================== */ 14 | #define be_vector_data(vector) ((vector)->data) 15 | #define be_vector_first(vector) ((vector)->data) 16 | #define be_vector_isend(vector, item) ((item) > (vector)->end) 17 | #define be_vector_isempty(vector) (!(vector)->count) 18 | #define be_vector_end(vector) ((vector)->end) 19 | #define be_vector_count(vector) ((vector)->count) 20 | #define be_vector_capacity(vector) ((vector)->capacity) 21 | #define be_stack_init(vm, stack, size) be_vector_init(vm, stack, size) 22 | #define be_stack_delete(vm, stack) be_vector_delete(vm, stack) 23 | #define be_stack_clear(stack) be_vector_clear(stack) 24 | #define be_stack_push(vm, stack, data) be_vector_push(vm, stack, data) 25 | #define be_stack_pop(stack) be_vector_remove_end(stack) 26 | #define be_stack_top(stack) be_vector_end(stack) 27 | #define be_stack_base(stack) be_vector_first(stack) 28 | #define be_stack_count(stack) be_vector_count(stack) 29 | #define be_stack_isempty(stack) be_vector_isempty(stack) 30 | 31 | /* ========================== function extern ========================== */ 32 | void be_vector_init(bvm *vm, bvector *vector, int size); 33 | void be_vector_delete(bvm *vm, bvector *vector); 34 | void* be_vector_at(bvector *vector, int index); 35 | void be_vector_push(bvm *vm, bvector *vector, void *data); 36 | void be_vector_push_c(bvm *vm, bvector *vector, void *data); 37 | void be_vector_remove_end(bvector *vector); 38 | void be_vector_resize(bvm *vm, bvector *vector, int count); 39 | void be_vector_clear(bvector *vector); 40 | void* be_vector_release(bvm *vm, bvector *vector); 41 | int be_nextsize(int value); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /tests/math.be: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | #- nan -# 4 | n = math.nan 5 | assert(str(n) == 'nan') 6 | assert(math.isnan(n)) 7 | assert(math.isnan(n+n)) 8 | assert(math.isnan(n+1)) 9 | assert(math.isnan(n*0)) 10 | 11 | assert(!math.isnan(0)) 12 | assert(!math.isnan(1.5)) 13 | assert(!math.isnan(math.inf)) 14 | 15 | assert(n != n) #- nan is never identical to itself -# 16 | 17 | #- inf -# 18 | i = math.inf 19 | assert(str(i) == 'inf') 20 | assert(str(-i) == '-inf') # inf has a sign 21 | assert(math.isinf(i)) 22 | assert(math.isinf(i+i)) 23 | assert(math.isinf(i+1)) 24 | # the following result in nan 25 | assert(math.isnan(i*0)) 26 | assert(math.isnan(i-i)) 27 | assert(math.isnan(i/i)) 28 | 29 | assert(!math.isinf(0)) 30 | assert(!math.isinf(1.5)) 31 | assert(!math.isinf(math.nan)) 32 | 33 | assert(i == i) #- equality works for inf -# 34 | assert(i == i+1) 35 | 36 | #- nan and inf convert to null in json -# 37 | import json 38 | 39 | m_nan = {"v": math.nan} 40 | assert(json.dump(m_nan) == '{"v":null}') 41 | m_inf1 = {"v": math.inf} 42 | assert(json.dump(m_inf1) == '{"v":null}') 43 | m_inf2 = {"v": -math.inf} 44 | assert(json.dump(m_inf2) == '{"v":null}') 45 | m_v = {"v": 3.5} 46 | assert(json.dump(m_v) == '{"v":3.5}') 47 | 48 | # math.round 49 | assert(math.round(3) == 3) 50 | assert(math.round(3.2) == 3) 51 | assert(math.round(3.5) == 4) 52 | assert(math.round(3.6) == 4) 53 | 54 | assert(math.round(-3) == -3) 55 | assert(math.round(-3.2) == -3) 56 | assert(math.round(-3.5) == -4) 57 | assert(math.round(-3.6) == -4) 58 | 59 | assert(math.round() == 0) 60 | 61 | #- min and max -# 62 | def assert_error(f, error_type) 63 | try 64 | f() 65 | assert(false, 'unexpected execution flow') 66 | except .. as e, m 67 | assert(e == error_type) 68 | end 69 | end 70 | 71 | assert(math.min() == nil) 72 | assert(math.min(0) == 0) 73 | assert(math.min(0,2,10,56) == 0) 74 | assert(math.min(4, 2, -10, 3) == -10) 75 | assert(math.min(4, 2) == 2) 76 | 77 | # result is int unless one of the parameters is real 78 | assert(type(math.min(4, 2, -10, 3)) == 'int') 79 | assert(type(math.min(4, 2, -10.0, 3)) == 'real') 80 | 81 | assert(math.min(-3.4, 5) == -3.4) 82 | 83 | # test invalid parameters 84 | assert_error(def () return math.min(4, nil) end, 'type_error') 85 | assert_error(def () return math.min(4, "", 4.5) end, 'type_error') 86 | -------------------------------------------------------------------------------- /src/be_code.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_CODE_H 9 | #define BE_CODE_H 10 | 11 | #include "be_parser.h" 12 | 13 | #define be_code_freeregs(f, n) ((f)->freereg -= (bbyte)(n)) 14 | 15 | int be_code_allocregs(bfuncinfo *finfo, int count); 16 | void be_code_prebinop(bfuncinfo *finfo, int op, bexpdesc *e); 17 | void be_code_binop(bfuncinfo *finfo, int op, bexpdesc *e1, bexpdesc *e2, int dst); 18 | int be_code_unop(bfuncinfo *finfo, int op, bexpdesc *e); 19 | int be_code_setvar(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2, bbool keep_reg); 20 | int be_code_nextreg(bfuncinfo *finfo, bexpdesc *e); 21 | int be_code_jump(bfuncinfo *finfo); 22 | void be_code_jumpto(bfuncinfo *finfo, int dst); 23 | void be_code_jumpbool(bfuncinfo *finfo, bexpdesc *e, int jumptrue); 24 | void be_code_conjump(bfuncinfo *finfo, int *list, int jmp); 25 | void be_code_patchlist(bfuncinfo *finfo, int list, int dst); 26 | void be_code_patchjump(bfuncinfo *finfo, int jmp); 27 | int be_code_getmethod(bfuncinfo *finfo, bexpdesc *e); 28 | void be_code_call(bfuncinfo *finfo, int base, int argc); 29 | int be_code_proto(bfuncinfo *finfo, bproto *proto); 30 | void be_code_closure(bfuncinfo *finfo, bexpdesc *e, int idx); 31 | void be_code_close(bfuncinfo *finfo, int isret); 32 | void be_code_class(bfuncinfo *finfo, bexpdesc *dst, bclass *c); 33 | void be_code_ret(bfuncinfo *finfo, bexpdesc *e); 34 | int be_code_resolve(bfuncinfo *finfo, bexpdesc *k); 35 | void be_code_member(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2); 36 | void be_code_index(bfuncinfo *finfo, bexpdesc *c, bexpdesc *k); 37 | void be_code_setsuper(bfuncinfo *finfo, bexpdesc *c, bexpdesc *s); 38 | void be_code_import(bfuncinfo *finfo, bexpdesc *m, bexpdesc *v); 39 | int be_code_exblk(bfuncinfo *finfo, int depth); 40 | void be_code_catch(bfuncinfo *finfo, int base, int ecnt, int vcnt, int *jmp); 41 | void be_code_raise(bfuncinfo *finfo, bexpdesc *e1, bexpdesc *e2); 42 | void be_code_implicit_class(bfuncinfo *finfo, bexpdesc *e, bclass *c); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/be_byteslib.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang - Stephan Hadinger 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef __BE_BYTESLIB_H 9 | #define __BE_BYTESLIB_H 10 | 11 | #include "be_object.h" 12 | 13 | #define BYTES_DEFAULT_SIZE 28 /* default pre-reserved size for buffer (keep 4 bytes for len/size) */ 14 | #define BYTES_OVERHEAD 4 /* bytes overhead to be added when allocating (used to store len and size) */ 15 | #define BYTES_HEADROOM 8 /* keep a natural headroom of 8 bytes when resizing */ 16 | 17 | #define BYTES_SIZE_FIXED -1 /* if size is -1, then the bytes object cannot be reized */ 18 | #define BYTES_SIZE_MAPPED -2 /* if size is -2, then the bytes object is mapped to a fixed memory region, i.e. cannot be resized */ 19 | #define BYTES_SIZE_SOLIDIFIED -3 /* is size is -3, then the bytes object is solidified and cannot be resized nor modified */ 20 | 21 | #define BYTES_RESIZE_ERROR "attribute_error" 22 | #define BYTES_RESIZE_MESSAGE "bytes object size is fixed and cannot be resized" 23 | #define BYTES_READ_ONLY_MESSAGE "bytes object is read only" 24 | /* be_raise(vm, BYTES_RESIZE_ERROR, BYTES_RESIZE_MESSAGE); */ 25 | 26 | typedef struct buf_impl { 27 | int32_t size; // size in bytes of the buffer 28 | int32_t len; // current size of the data in buffer. Invariant: len <= size 29 | uint8_t *bufptr; // the actual data 30 | int32_t prev_size; // previous value read from the instance 31 | int32_t prev_len; // previous value read from the instance 32 | uint8_t *prev_bufptr; 33 | bbool fixed; // is size fixed? (actually encoded as negative size) 34 | bbool mapped; 35 | bbool solidified; 36 | } buf_impl; 37 | 38 | size_t be_bytes_tohex(char * out, size_t outsz, const uint8_t * in, size_t insz); 39 | 40 | #if BE_USE_PRECOMPILED_OBJECT 41 | #include "../generate/be_const_bytes.h" 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /tools/grammar/berry.ebnf: -------------------------------------------------------------------------------- 1 | (* program define *) 2 | program = block; 3 | (* block define *) 4 | block = {statement}; 5 | (* statement define *) 6 | statement = class_stmt | func_stmt | var_stmt | if_stmt | while_stmt | 7 | for_stmt | break_stmt | return_stmt | expr_stmt | import_stmt | 8 | try_stmt | throw_stmt | do_stmt | ';'; 9 | if_stmt = 'if' expr block {'elif' expr block} ['else' block] 'end'; 10 | while_stmt = 'while' expr block 'end'; 11 | do_stmt = 'do' block 'end'; 12 | for_stmt = 'for' ID ':' expr block 'end'; 13 | break_stmt = 'break' | 'continue'; 14 | return_stmt = 'return' [expr]; 15 | (* function define statement *) 16 | func_stmt = 'def' ID func_body; 17 | func_body = '(' [arg_field {',' arg_field}] ')' block 'end'; 18 | arg_field = ['*'] ID; 19 | (* class define statement *) 20 | class_stmt = 'class' ID [':' ID] class_block 'end'; 21 | class_block = {'var' ID {',' ID} | 'static' ['var'] ID ['=' expr] {',' ID ['=' expr] } | 'static' func_stmt | func_stmt}; 22 | import_stmt = 'import' (ID (['as' ID] | {',' ID}) | STRING 'as' ID); 23 | (* exceptional handling statement *) 24 | try_stmt = 'try' block except_block {except_block} 'end'; 25 | except_block = except_stmt block; 26 | except_stmt = 'except' (expr {',' expr} | '..') ['as' ID [',' ID]]; 27 | throw_stmt = 'raise' expr [',' expr]; 28 | (* variable define statement *) 29 | var_stmt = 'var' ID ['=' expr] {',' ID ['=' expr]}; 30 | (* expression define *) 31 | expr_stmt = expr [assign_op expr]; 32 | expr = suffix_expr | unop expr | expr binop expr | range_expr | cond_expr | walrus_expr; 33 | cond_expr = expr '?' expr ':' expr; (* conditional expression *) 34 | assign_op = '=' | '+=' | '-=' | '*=' | '/=' | 35 | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>='; 36 | binop = '<' | '<=' | '==' | '!=' | '>' | '>=' | '||' | '&&' | 37 | '<<' | '>>' | '&' | '|' | '^' | '+' | '-' | '*' | '/' | '%'; 38 | range_expr = expr '..' [expr] 39 | unop = '-' | '!' | '~'; 40 | walrus_expr = expr ':=' expr 41 | suffix_expr = primary_expr {call_expr | ('.' ID) | '[' expr ']'}; 42 | primary_expr = '(' expr ')' | simple_expr | list_expr | map_expr | anon_func | lambda_expr; 43 | simple_expr = INTEGER | REAL | STRING | ID | 'true' | 'false' | 'nil' | f_string; 44 | f_string = 'f' STRING 45 | call_expr = '(' [expr {',' expr}] ')'; 46 | list_expr = '[' {expr ','} [expr] ']'; 47 | map_expr = '{' {expr ':' expr ','} [expr ':' expr] '}'; 48 | anon_func = 'def' func_body; (* anonymous function *) 49 | lambda_expr = '/' [arg_field {',' arg_field}] | {arg_field}] '->' expr; 50 | -------------------------------------------------------------------------------- /tests/walrus.be: -------------------------------------------------------------------------------- 1 | # test for the walrus operator 2 | var a = 1 3 | assert((a := a + 1) == 2) 4 | assert((a := a + 1) == 3) 5 | 6 | def f() 7 | var defer = 10 8 | var ret = [] 9 | for i:0..100 10 | if (defer := defer - 1) == 0 11 | ret.push(i) 12 | defer = 10 13 | end 14 | end 15 | return ret 16 | end 17 | assert(f() == [9, 19, 29, 39, 49, 59, 69, 79, 89, 99]) 18 | 19 | # test for expressions with no side effects 20 | def assert_attribute_error(c) 21 | try 22 | compile(c)() 23 | assert(false, 'unexpected execution flow') 24 | except 'syntax_error' as e, m 25 | end 26 | end 27 | 28 | # below the expressions `a` or `a b` have no side effect and do not generate any code, this is an error when in strict mode 29 | import strict 30 | var a, b 31 | assert_attribute_error("var a,b def f() a b end") 32 | assert_attribute_error("var a,b def f() a end") 33 | 34 | # while the following does have side effect 35 | def f() a := b end 36 | 37 | # bug when using walrus with member 38 | def id(x) return x end 39 | var a = 1 40 | import global 41 | def f() return id(global.a := 42) end 42 | assert(f() == 42) 43 | # bug: returns 44 | 45 | def concat(x, y, z) return str(x)+str(y)+str(z) end 46 | var a = 1 47 | import global 48 | def f() return concat(global.a := 1, global.a := 42, global.a := 0) end 49 | assert(f() == "1420") 50 | # bug: returns '142' 51 | 52 | # same bug when using index 53 | def id(x) return x end 54 | l = [10,11] 55 | import global 56 | def f() return id(global.l[0] := 42) end 57 | assert(f() == 42) 58 | # bug: returns [42, 11] 59 | 60 | # bug when using member for self 61 | class confused_walrus 62 | var b 63 | def f() 64 | var c = 1 65 | if self.b := true 66 | c = 2 67 | end 68 | return self 69 | end 70 | end 71 | var ins = confused_walrus() 72 | assert(ins.f() == ins) 73 | 74 | # Check overwriting a builtin (https://github.com/berry-lang/berry/issues/416) 75 | 76 | def check_overwrite_builtin() 77 | print := 1 78 | assert(print == 1) 79 | end 80 | 81 | check_overwrite_builtin() 82 | 83 | # Bug when dereferencing in a walrus 84 | class Test_walrus_member 85 | var a 86 | def init() 87 | self.a = 2 88 | end 89 | def f() 90 | var v 91 | if (v := self.a) != nil # here is where the bug happens 92 | return v # 'v' would have a wrong value 93 | end 94 | end 95 | end 96 | var t = Test_walrus_member() 97 | assert(t.f() == 2) 98 | -------------------------------------------------------------------------------- /default/be_modtab.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "berry.h" 9 | 10 | /* this file contains the declaration of the module table. */ 11 | 12 | /* default modules declare */ 13 | be_extern_native_module(string); 14 | be_extern_native_module(json); 15 | be_extern_native_module(math); 16 | be_extern_native_module(time); 17 | be_extern_native_module(os); 18 | be_extern_native_module(global); 19 | be_extern_native_module(sys); 20 | be_extern_native_module(debug); 21 | be_extern_native_module(gc); 22 | be_extern_native_module(solidify); 23 | be_extern_native_module(introspect); 24 | be_extern_native_module(strict); 25 | be_extern_native_module(undefined); 26 | 27 | /* user-defined modules declare start */ 28 | 29 | /* user-defined modules declare end */ 30 | 31 | /* module list declaration */ 32 | BERRY_LOCAL const bntvmodule_t* const be_module_table[] = { 33 | /* default modules register */ 34 | #if BE_USE_STRING_MODULE 35 | &be_native_module(string), 36 | #endif 37 | #if BE_USE_JSON_MODULE 38 | &be_native_module(json), 39 | #endif 40 | #if BE_USE_MATH_MODULE 41 | &be_native_module(math), 42 | #endif 43 | #if BE_USE_TIME_MODULE 44 | &be_native_module(time), 45 | #endif 46 | #if BE_USE_OS_MODULE 47 | &be_native_module(os), 48 | #endif 49 | #if BE_USE_GLOBAL_MODULE 50 | &be_native_module(global), 51 | #endif 52 | #if BE_USE_SYS_MODULE 53 | &be_native_module(sys), 54 | #endif 55 | #if BE_USE_DEBUG_MODULE 56 | &be_native_module(debug), 57 | #endif 58 | #if BE_USE_GC_MODULE 59 | &be_native_module(gc), 60 | #endif 61 | #if BE_USE_SOLIDIFY_MODULE 62 | &be_native_module(solidify), 63 | #endif 64 | #if BE_USE_INTROSPECT_MODULE 65 | &be_native_module(introspect), 66 | #endif 67 | #if BE_USE_STRICT_MODULE 68 | &be_native_module(strict), 69 | #endif 70 | &be_native_module(undefined), 71 | /* user-defined modules register start */ 72 | 73 | /* user-defined modules register end */ 74 | NULL /* do not remove */ 75 | }; 76 | 77 | /* user-defined classes declare start */ 78 | /* be_extern_native_class(my_class); */ 79 | /* user-defined classes declare end */ 80 | 81 | BERRY_LOCAL bclass_array be_class_table = { 82 | /* first list are direct classes */ 83 | /* &be_native_class(my_class), */ 84 | NULL, /* do not remove */ 85 | }; 86 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -Wextra -std=c99 -pedantic-errors -O2 2 | DEBUG_FLAGS = -O0 -g -DBE_DEBUG 3 | TEST_FLAGS = $(DEBUG_FLAGS) --coverage -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined 4 | LIBS = -lm 5 | TARGET = berry 6 | CC = gcc 7 | MKDIR = mkdir 8 | LFLAGS = 9 | PREFIX = /usr/local 10 | BINDIR = $(PREFIX)/bin 11 | 12 | INCPATH = src default 13 | SRCPATH = src default 14 | GENERATE = generate 15 | CONFIG = default/berry_conf.h 16 | COC = tools/coc/coc 17 | CONST_TAB = $(GENERATE)/be_const_strtab.h 18 | 19 | ifeq ($(OS), Windows_NT) # Windows 20 | CFLAGS += -Wno-format # for "%I64d" warning 21 | LFLAGS += -Wl,--out-implib,berry.lib # export symbols lib for dll linked 22 | TARGET := $(TARGET).exe 23 | PYTHON ?= python # only for windows and need python3 24 | COC := $(PYTHON) $(COC) 25 | else 26 | CFLAGS += -DUSE_READLINE_LIB 27 | LIBS += -lreadline -ldl 28 | OS := $(shell uname) 29 | ifeq ($(OS), Linux) 30 | LFLAGS += -Wl,--export-dynamic 31 | endif 32 | endif 33 | 34 | ifneq ($(V), 1) 35 | Q=@ 36 | MSG=@echo 37 | else 38 | MSG=@true 39 | endif 40 | 41 | SRCS = $(foreach dir, $(SRCPATH), $(wildcard $(dir)/*.c)) 42 | OBJS = $(patsubst %.c, %.o, $(SRCS)) 43 | DEPS = $(patsubst %.c, %.d, $(SRCS)) 44 | INCFLAGS = $(foreach dir, $(INCPATH), -I"$(dir)") 45 | 46 | .PHONY : clean 47 | 48 | all: $(TARGET) 49 | 50 | debug: CFLAGS += $(DEBUG_FLAGS) 51 | debug: all 52 | 53 | test: CFLAGS += $(TEST_FLAGS) 54 | test: LFLAGS += $(TEST_FLAGS) 55 | test: all 56 | $(MSG) [Run Testcases...] 57 | $(Q) ./testall.be 58 | $(Q) $(RM) */*.gcno */*.gcda 59 | 60 | $(TARGET): $(OBJS) 61 | $(MSG) [Linking...] 62 | $(Q) $(CC) $(OBJS) $(LFLAGS) $(LIBS) -o $@ 63 | $(MSG) done 64 | 65 | $(OBJS): %.o: %.c 66 | $(MSG) [Compile] $< 67 | $(Q) $(CC) -MM $(CFLAGS) $(INCFLAGS) -MT"$*.d" -MT"$(<:.c=.o)" $< > $*.d 68 | $(Q) $(CC) $(CFLAGS) $(INCFLAGS) -c $< -o $@ 69 | 70 | sinclude $(DEPS) 71 | 72 | $(OBJS): $(CONST_TAB) 73 | 74 | $(CONST_TAB): $(GENERATE) $(SRCS) $(CONFIG) 75 | $(MSG) [Prebuild] generate resources 76 | $(Q) $(COC) -o $(GENERATE) $(SRCPATH) -c $(CONFIG) 77 | 78 | $(GENERATE): 79 | $(Q) $(MKDIR) $(GENERATE) 80 | 81 | install: 82 | mkdir -p $(DESTDIR)$(BINDIR) 83 | cp $(TARGET) $(DESTDIR)$(BINDIR)/$(TARGET) 84 | 85 | uninstall: 86 | $(RM) $(DESTDIR)$(BINDIR)/$(TARGET) 87 | 88 | prebuild: $(GENERATE) 89 | $(MSG) [Prebuild] generate resources 90 | $(Q) $(COC) -o $(GENERATE) $(SRCPATH) -c $(CONFIG) 91 | $(MSG) done 92 | 93 | clean: 94 | $(MSG) [Clean...] 95 | $(Q) $(RM) $(OBJS) $(DEPS) $(GENERATE)/* berry.lib 96 | $(MSG) done 97 | -------------------------------------------------------------------------------- /src/be_object.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | #include "be_exec.h" 10 | #include "be_mem.h" 11 | #include "be_gc.h" 12 | #include "be_vm.h" 13 | 14 | #define cast_comobj(o) gc_cast(o, BE_COMOBJ, bcommomobj) 15 | 16 | const char* be_vtype2str(bvalue *v) 17 | { 18 | switch(var_primetype(v)) { 19 | case BE_NIL: return "nil"; 20 | case BE_INT: return "int"; 21 | case BE_REAL: return "real"; 22 | case BE_BOOL: return "bool"; 23 | case BE_CLOSURE: case BE_NTVCLOS: case BE_CTYPE_FUNC: 24 | case BE_NTVFUNC: return "function"; 25 | case BE_PROTO: return "proto"; 26 | case BE_CLASS: return "class"; 27 | case BE_STRING: return "string"; 28 | case BE_LIST: return "list"; 29 | case BE_MAP: return "map"; 30 | case BE_INSTANCE: return "instance"; 31 | case BE_MODULE: return "module"; 32 | case BE_INDEX: return "var"; 33 | case BE_COMPTR: return "ptr"; 34 | default: return "invalid type"; 35 | } 36 | } 37 | 38 | bvalue* be_indexof(bvm *vm, int idx) 39 | { 40 | if (idx > 0) { /* absolute index */ 41 | be_assert(vm->reg + idx <= vm->top); 42 | return vm->reg + idx - 1; 43 | } 44 | /* relative index */ 45 | be_assert(vm->top + idx >= vm->reg); 46 | return vm->top + idx; 47 | } 48 | 49 | BERRY_API void be_newcomobj(bvm *vm, void *data, bntvfunc destroy) 50 | { 51 | bcommomobj *obj; 52 | bgcobject *gco = be_gcnew(vm, BE_COMOBJ, bcommomobj); 53 | if ((obj = cast_comobj(gco)) != NULL) { 54 | bvalue* top = be_incrtop(vm); 55 | obj->data = data; 56 | obj->destroy = destroy; 57 | var_setobj(top, BE_COMOBJ, obj); 58 | } 59 | } 60 | 61 | void be_commonobj_delete(bvm *vm, bgcobject *obj) 62 | { 63 | bcommomobj *co = cast_comobj(obj); 64 | if (co) { 65 | if (co->destroy && co->data) { 66 | be_pushntvfunction(vm, co->destroy); 67 | be_pushcomptr(vm, co->data); 68 | be_call(vm, 1); 69 | be_pop(vm, 2); 70 | } 71 | be_free(vm, co, sizeof(bcommomobj)); 72 | } 73 | } 74 | 75 | /* generic destroy method for comobj, just call be_os_free() on the pointer */ 76 | int be_commonobj_destroy_generic(bvm* vm) 77 | { 78 | int argc = be_top(vm); 79 | if (argc > 0) { 80 | void * obj = be_tocomptr(vm, 1); 81 | if (obj != NULL) { be_os_free(obj); } 82 | } 83 | be_return_nil(vm); 84 | } 85 | -------------------------------------------------------------------------------- /tools/coc/coc: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import re 4 | import os 5 | from coc_parser import * 6 | from str_build import * 7 | from bytes_build import * 8 | from block_builder import * 9 | from macro_table import * 10 | 11 | class builder: 12 | Input = 0 13 | Output = 1 14 | Config = 2 15 | 16 | def __init__(self, input_folders, output_folder, macro_files): 17 | self.output = output_folder 18 | self.input = input_folders 19 | self.config = macro_files 20 | self.macro = None 21 | self.strmap = {} 22 | self.strmap_weak = {} 23 | self.strmap_long = {} 24 | self.bytesmap = {} 25 | 26 | self.macro = macro_table() 27 | for path in self.config: 28 | self.macro.scan_file(path) 29 | 30 | for d in self.input: 31 | self.scandir(d) 32 | 33 | sb = str_build(self.strmap, self.strmap_weak, self.strmap_long) 34 | sb.build(self.output) 35 | 36 | sbytes = bytes_build(self.bytesmap) 37 | sbytes.build(self.output) 38 | 39 | def parse_file(self, filename): 40 | if re.search(r"\.(h|c|cc|cpp)$", filename): 41 | # print(f"> parse {filename}") 42 | text = "" 43 | with open(filename, encoding='utf-8') as f: 44 | text = f.read() 45 | # print(f"> len(text)={len(text)}") 46 | parser = coc_parser(text) 47 | for s in parser.strtab: 48 | self.strmap[s] = 0 49 | for s in parser.strtab_weak: 50 | self.strmap_weak[s] = 0 51 | for s in parser.strtab_long: 52 | self.strmap_long[s] = 0 53 | for s in parser.bintab: 54 | self.bytesmap[s] = 0 55 | for obj in parser.objects: 56 | builder = block_builder(obj, self.macro) 57 | for s in builder.strtab: 58 | self.strmap[s] = 0 59 | for s in builder.strtab_weak: 60 | self.strmap_weak[s] = 0 61 | for s in builder.strtab_long: 62 | self.strmap_long[s] = 0 63 | builder.dumpfile(self.output) 64 | 65 | def scandir(self, srcpath): 66 | for item in os.listdir(srcpath): 67 | path = os.path.join(srcpath, item) 68 | if os.path.isfile(path): self.parse_file(path) 69 | 70 | 71 | if __name__ == '__main__': 72 | import argparse 73 | parser = argparse.ArgumentParser() 74 | parser.add_argument("input_folder", nargs='+', help='folders containing the C/C++ files to be parsed') 75 | parser.add_argument("-o", help='output folder', required=True) 76 | parser.add_argument("-c", nargs='+', help='configuration folders for preprocessor') 77 | 78 | args = vars(parser.parse_args()) 79 | # print(args) 80 | b = builder(args["input_folder"], args["o"], args["c"]) 81 | -------------------------------------------------------------------------------- /tests/class_const.be: -------------------------------------------------------------------------------- 1 | def assert_attribute_error(f) 2 | try 3 | f() 4 | assert(false, 'unexpected execution flow') 5 | except .. as e, m 6 | assert(e == 'attribute_error') 7 | end 8 | end 9 | 10 | class A 11 | static var a 12 | def init() self.b = 2 end 13 | def f() end 14 | var b 15 | static var c, s, r 16 | end 17 | 18 | assert(A.a == nil) 19 | assert(A.c == nil) 20 | assert(A.s == nil) 21 | assert_attribute_error(/-> A.b) 22 | assert_attribute_error(/-> A.d) 23 | 24 | a = A() 25 | assert(a.b == 2) 26 | assert(a.a == nil) 27 | assert(a.c == nil) 28 | 29 | A.a = 1 30 | A.c = 3 31 | A.s = "foo" 32 | A.r = 3.5 33 | assert(a.a == 1) 34 | assert(a.c == 3) 35 | assert(A.a == 1) 36 | assert(A.c == 3) 37 | import gc gc.collect() 38 | assert(A.s == "foo") 39 | assert(a.s == "foo") 40 | assert(A.r == 3.5) 41 | assert(a.r == 3.5) 42 | 43 | #- test valid or invalid methods and members -# 44 | 45 | def assert_attribute_error(c) 46 | try 47 | compile(c)() 48 | assert(false, 'unexpected execution flow') 49 | except .. as e, m 50 | assert(e == 'attribute_error') 51 | end 52 | end 53 | 54 | class A 55 | var a, g 56 | static h 57 | def init() self.a = 1 end 58 | def f(x, y) return type(self) end 59 | end 60 | a=A() 61 | a.g = def (x, y) return type(x) end 62 | A.h = def (x, y) return type(x) end 63 | 64 | assert(type(a.g) == 'function') 65 | assert(type(a.h) == 'function') 66 | 67 | assert(a.g(1) == 'int') 68 | assert(a.h(1) == 'int') 69 | assert(A.h(1) == 'int') 70 | 71 | 72 | class A 73 | var a 74 | static def g(x, y) return [x,y] end 75 | static h = def (x, y) return [x,y] end 76 | def init() self.a = 1 end 77 | def f(x, y) return type(self) end 78 | end 79 | a=A() 80 | assert(type(a.g) == 'function') 81 | assert(type(a.h) == 'function') 82 | assert(type(A.g) == 'function') 83 | assert(type(A.h) == 'function') 84 | assert(a.g(1,2) == [1,2]) 85 | assert(a.h(1,2) == [1,2]) 86 | assert(A.g(1,2) == [1,2]) 87 | assert(A.h(1,2) == [1,2]) 88 | a.a = def (x,y) return [x,y] end 89 | assert(a.a(1,2) == [1,2]) 90 | 91 | 92 | #- test static initializers -# 93 | class A 94 | static a = 1, b, c = 3.5, d = 42, e = "foo", f = [1], g = {} 95 | var aa,ab 96 | end 97 | 98 | assert(A.a == 1) 99 | assert(A.b == nil) 100 | assert(A.c == 3.5) 101 | assert(A.d == 42) 102 | assert(A.e == "foo") 103 | assert(A.f == [1]) 104 | 105 | a = A() 106 | assert(a.a == 1) 107 | assert(a.b == nil) 108 | assert(a.c == 3.5) 109 | assert(a.d == 42) 110 | assert(a.e == "foo") 111 | assert(a.f == [1]) 112 | assert(a.g == A.g) 113 | assert(a.aa == nil) 114 | assert(a.ab == nil) 115 | 116 | #- used to fail for subclasses -# 117 | class A static a=1 end 118 | class B:A static a=A def f() end static b=1 static c=A end 119 | assert(A.a == 1) 120 | assert(B.a == A) 121 | assert(B.b == 1) 122 | assert(B.c == A) 123 | -------------------------------------------------------------------------------- /tests/json.be: -------------------------------------------------------------------------------- 1 | import json 2 | import string 3 | # load tests 4 | 5 | def assert_load(text, value) 6 | var loaded_val = json.load(text) 7 | var ok = loaded_val == value 8 | if !ok 9 | print(string.format('for JSON \'%s\' expected %s [%s] but got %s [%s]', text, str(value), type(value), str(loaded_val), type(loaded_val))) 10 | end 11 | assert(ok) 12 | end 13 | 14 | def assert_load_failed(text) 15 | assert(json.load(text) == nil) 16 | end 17 | 18 | assert_load('null', nil) 19 | assert_load('true', true) 20 | assert_load('false', false) 21 | assert_load('123', 123) 22 | assert_load('12.3', 12.3) 23 | assert_load('-0.1', -0.1) 24 | assert_load('1e2', 1e2) 25 | assert_load('1e+2', 1e+2) 26 | assert_load('1e-2', 1e-2) 27 | assert_load('1E2', 1e2) 28 | assert_load('1E+2', 1e+2) 29 | assert_load('1.2e7', 1.2e7) 30 | assert_load('"abc"', 'abc') 31 | # strings 32 | assert_load('"\\"\\\\\\/\\b\\f\\n\\r\\t"', '\"\\/\b\f\n\r\t') 33 | assert_load('"\\u1234\\u2345\\u04aF\\u003A"', 'ሴ⍅ү:') 34 | assert_load_failed('"\\u3fr"'); 35 | assert_load_failed('"\\q"'); 36 | assert_load_failed('"123'); 37 | # list 38 | assert_load('[1, null]', [1, nil]) 39 | assert_load_failed('[x]') 40 | assert_load_failed('[1, nil]') 41 | assert_load_failed('[1, null') 42 | # object 43 | var o = json.load('{"key": 1}') 44 | assert(o['key'] == 1 && o.size() == 1) 45 | 46 | # parsing an empty string used to cause berry to pass a NULL to strncmp 47 | # make sure we catch this 48 | o = json.load('{"key": ""}') 49 | assert(o['key'] == '' && o.size() == 1) 50 | 51 | assert_load_failed('{"ke: 1}') 52 | assert_load_failed('{"key": 1x}') 53 | assert_load_failed('{"key"}') 54 | assert_load_failed('{"key": 1, }') 55 | # insanely long, nested object 56 | var text = 'null' 57 | for i : 0 .. 200 58 | text = '{"nested":' + text + ', "num": 1, "bool": true, "str": "abc", "n": null, "arr": [1, 2, 3]}' 59 | end 60 | json.load(text) # do nothing, just check that it doesn't crash 61 | 62 | # dump tests 63 | 64 | def assert_dump(value, text, format) 65 | assert(json.dump(value, format) == text) 66 | end 67 | 68 | assert_dump(nil, 'null'); 69 | assert_dump(true, 'true'); 70 | assert_dump(false, 'false'); 71 | assert_dump(1.23, '1.23'); 72 | assert_dump('String', '"String"'); 73 | assert_dump([1, 'x'], '[1,"x"]'); 74 | assert_dump({1: 'x'}, '{"1":"x"}'); 75 | assert_dump([1, 'x'], '[\n 1,\n "x"\n]', 'format'); 76 | assert_dump({1: 'x'}, '{\n "1": "x"\n}', 'format'); 77 | assert_dump({1: 'x', 'k': 'v'}, '{"k":"v","1":"x"}'); 78 | 79 | class map2 : map def init() super(self).init() end end 80 | var m = map2() 81 | m['key'] = 1 82 | assert_dump(m, '{"key":1}') 83 | 84 | # sweep dumping nested arrays of diffrent sizes 85 | # this tests for any unexpanded stack conditions 86 | for count : 10..200 87 | var arr = [[]] 88 | var last_arr = arr 89 | for i : 0..count 90 | var pushed = [i] 91 | last_arr.push(pushed) 92 | last_arr = pushed 93 | end 94 | json.dump(arr) 95 | end 96 | -------------------------------------------------------------------------------- /tests/super_auto.be: -------------------------------------------------------------------------------- 1 | #- test for new auto class inference of super() -# 2 | 3 | #- test that we can call init() even if it's not defined -# 4 | class Z end 5 | z=Z() 6 | assert(z.init != nil) 7 | z.init() #- should do nothing -# 8 | 9 | #- check the old way still works -# 10 | class A1 11 | var a 12 | def init(a) 13 | self.a = a 14 | end 15 | end 16 | class B1:A1 17 | var b 18 | def init(a,b) 19 | super(self,A1).init(a) 20 | self.b = b 21 | end 22 | end 23 | class C1:B1 24 | var c 25 | def init(a,b,c) 26 | super(self,B1).init(a,b) 27 | self.c = c 28 | end 29 | end 30 | #- -# 31 | c1=C1(1,2,3) 32 | assert(c1.a == 1) 33 | assert(c1.b == 2) 34 | assert(c1.c == 3) 35 | 36 | #- test simple behavior -# 37 | class A0 var a end 38 | class B0:A0 var b end 39 | class C0:B0 end 40 | c0=C0() 41 | assert(classof(c0) == C0) 42 | assert(classof(super(c0)) == B0) 43 | assert(classof(super(super(c0))) == A0) 44 | assert(super(super(super(c0))) == nil) 45 | 46 | assert(super(C0) == B0) 47 | assert(super(super(C0)) == A0) 48 | assert(super(super(super(C0))) == nil) 49 | 50 | assert(classof(super(c0,B0)) == B0) 51 | assert(classof(super(c0,A0)) == A0) 52 | 53 | #- test auto inference of target superclass -# 54 | class A 55 | var a 56 | def init(a) 57 | self.a = a 58 | end 59 | end 60 | class B:A 61 | var b 62 | def init(a,b) 63 | super(self).init(a) 64 | self.b = b 65 | end 66 | end 67 | class C:B 68 | var c 69 | def init(a,b,c) 70 | super(self).init(a,b) 71 | self.c = c 72 | end 73 | end 74 | #- -# 75 | c=C(1,2,3) 76 | 77 | assert(c.a == 1) 78 | assert(c.b == 2) 79 | assert(c.c == 3)class A 80 | end 81 | class B:A 82 | var b 83 | def init(a,b) super(self).init(a) self.b = b end 84 | end 85 | class C:B 86 | var c 87 | def init(a,b,c) super(self).init(a,b) self.c = c end 88 | end 89 | c=C(1,2,3) 90 | 91 | #- variant if A2 does not have an init() method, still works -# 92 | class A2 93 | static a=1 94 | end 95 | class B2:A2 96 | var b 97 | def init(a,b) super(self).init(a) self.b = b end 98 | end 99 | class C2:B2 100 | var c 101 | def init(a,b,c) super(self).init(a,b) self.c = c end 102 | end 103 | #- -# 104 | c2=C2(1,2,3) 105 | assert(c2.a == 1) 106 | assert(c2.b == 2) 107 | assert(c2.c == 3) 108 | 109 | #- difference in behavior whether the second arg is provided or not -# 110 | class A3 111 | end 112 | class B3:A3 113 | def b1() 114 | return super(self) 115 | end 116 | def b2(c) 117 | return super(self, c) 118 | end 119 | end 120 | class C3:B3 121 | end 122 | #- -# 123 | b3=B3() 124 | c3=C3() 125 | assert(classof(c3.b1()) == A3) 126 | assert(classof(b3.b1()) == A3) 127 | assert(classof(c3.b2(B3)) == B3) 128 | assert(classof(c3.b2(A3)) == A3) 129 | 130 | assert(classof(c3.b2(nil)) == B3) #- testing super(self,nil) in B3::b2() -# 131 | 132 | assert(c3.b2(C3) == nil) #- if specifying the current class, can't find any relevant class in supers -# -------------------------------------------------------------------------------- /tools/grammar/berry.bytecode: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- the Berry's bytecode file structure (version 1) 3 | -- 4 | -- description 5 | -- 6 | -- a double dash ('--') start a line comment. 7 | -- a: b means that the entity 'a' is realized by the 'b' structure. 8 | -- a: b c 'b' and 'c' are arranged in a compact order (one byte alignment). 9 | -- [a] means that structure 'a' can be repeated 0 to any times. 10 | -- [a](b) means that structure 'a' can be repeated 'b' times. 11 | -- a | b means that the entity can be implemented by structure 'a' or 'b'. 12 | -- N a number indicates the byte count of the field (eg. 'a: 1'). 13 | -- .N means the number of bits in the field (eg. 'a: .1'). 14 | -- only the first entity is a file entity (the root). 15 | ------------------------------------------------------------------------------- 16 | 17 | bytecode_file: -- little endian 18 | header 19 | global_desc 20 | main_function 21 | 22 | header: 8 23 | magic_number: 3 -- 0xbecdfe (berry code file) 24 | version: 2 -- update with file structure definition 25 | integer_size: .1 26 | float_size: .1 27 | -- reserved space 28 | 29 | main_function -> function 30 | 31 | global_desc: 32 | builtin_count: 4 33 | global_count: 4 -- excluding builtins 34 | global_name -> [ 35 | string 36 | ](global_count) 37 | 38 | function: 39 | information: 40 | function_name: 41 | string 42 | source: 43 | string 44 | argc: 1 -- arguments count 45 | nstack: 1 -- number of stack size by this function 46 | varg: 1 47 | extra: 1 -- extra data 48 | bytecode: 49 | code_size: 4 50 | code_array -> [ -- bytecode array 51 | instruction: 4 52 | ](code_size) 53 | constant_table: 54 | constant_count: 4 55 | [constant_value](constant_count) 56 | proto_table: 57 | proto_count: 4 58 | [function](proto_count) 59 | upval_table: 60 | upval_count: 1 61 | upvals: [ 62 | instack: 1 63 | index: 1 64 | ](upval_count) 65 | debug_info: 66 | -- reserved 67 | 68 | constant_value: 69 | type: 1 -- type of value 70 | integer | float | string | class 71 | 72 | string: 73 | string_size: 2 74 | byte_array: string_size 75 | 76 | class: 77 | class_name: 78 | string 79 | member_count: 4 -- number of member variables 80 | method_count: 4 -- number of method 81 | method_table: [ 82 | string -- method name 83 | function | nil -- method function body or nil (static member) if the first byte is null (which would be an empty func name) 84 | ](method_count) 85 | member_index_table -> [ 86 | string -- member name 87 | ](member_count) 88 | 89 | nil: 1 90 | boolean: 1 91 | integer: 4 | 8 92 | float: 4 | 8 93 | -------------------------------------------------------------------------------- /src/be_parser.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_PARSER_H 9 | #define BE_PARSER_H 10 | 11 | #include "be_object.h" 12 | #include "be_string.h" 13 | 14 | typedef enum { 15 | ETVOID, /* unknown (new variable or error) */ 16 | ETNIL, 17 | ETBOOL, 18 | ETREAL, 19 | ETINT, 20 | ETSTRING, 21 | ETPROTO, 22 | ETCONST, 23 | ETLOCAL, /* local variable, allocated until end of scope */ 24 | ETGLOBAL, /* global by index number */ 25 | ETUPVAL, 26 | ETMEMBER, /* member accessor (by name) */ 27 | ETINDEX, /* index accessor (ex array index) */ 28 | ETREG, /* temporary register, can be freed if top of stack */ 29 | ETNGLOBAL /* named global */ 30 | } exptype_t; 31 | 32 | typedef struct { 33 | union { 34 | struct { /* for suffix */ 35 | unsigned int idx:9; /* suffix RK index */ 36 | unsigned int obj:9; /* object RK index */ 37 | unsigned int tt:5; /* object type */ 38 | } ss; 39 | breal r; /* for ETREAL */ 40 | bint i; /* for ETINT */ 41 | bstring *s; /* for ETSTRING */ 42 | bproto *p; /* for ETPROTO */ 43 | int idx; /* variable index */ 44 | } v; 45 | int t; /* patch list of 'exit when true' */ 46 | int f; /* patch list of 'exit when false' */ 47 | bbyte not; /* not mark */ 48 | exptype_t type; 49 | } bexpdesc; 50 | 51 | typedef struct bblockinfo { 52 | struct bblockinfo *prev; 53 | bbyte nactlocals; /* number of active local variables */ 54 | bbyte type; /* block type mask */ 55 | bbyte hasupval; /* has upvalue mark */ 56 | bbyte sideeffect; /* did the last expr/statement had a side effect */ 57 | int lastjmp; /* pc for the last jump, prevents false register optimizations */ 58 | int breaklist; /* break list */ 59 | int beginpc; /* begin pc */ 60 | int continuelist; /* continue list */ 61 | } bblockinfo; 62 | 63 | typedef struct bfuncinfo { 64 | struct bfuncinfo *prev; /* outer function */ 65 | bproto *proto; /* the function prototype */ 66 | bblockinfo *binfo; /* block information */ 67 | struct blexer *lexer; /* the lexer pointer */ 68 | blist *local; /* local variable */ 69 | bmap *upval; /* upvalue variable */ 70 | bvector code; /* code vector */ 71 | bvector kvec; /* constants table */ 72 | bvector pvec; /* proto table */ 73 | #if BE_DEBUG_RUNTIME_INFO /* debug information */ 74 | bvector linevec; 75 | #endif 76 | #if BE_DEBUG_VAR_INFO 77 | bvector varvec; 78 | #endif 79 | int pc; /* program count */ 80 | bbyte freereg; /* first free register */ 81 | bbyte flags; /* some flages */ 82 | } bfuncinfo; 83 | 84 | /* code block type definitions */ 85 | #define BLOCK_LOOP 1 86 | #define BLOCK_EXCEPT 2 87 | 88 | bclosure *be_parser_source(bvm *vm, 89 | const char *fname, breader reader, void *data, bbool islocal); 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /tests/call.be: -------------------------------------------------------------------------------- 1 | #- -# 2 | #- witness function dumping first 3 args -# 3 | #- -# 4 | def f(a,b,c) return [a,b,c] end 5 | 6 | #- simple calls with fixed args -# 7 | assert(call(f) == [nil, nil, nil]) 8 | assert(call(f, 1) == [1, nil, nil]) 9 | assert(call(f, 1, 2) == [1, 2, nil]) 10 | assert(call(f, 1, 2, 3, 4) == [1, 2, 3]) 11 | 12 | #- call with var args -# 13 | assert(call(f, []) == [nil, nil, nil]) 14 | assert(call(f, [1]) == [1, nil, nil]) 15 | assert(call(f, [1, 2]) == [1, 2, nil]) 16 | assert(call(f, [1, 2, 3, 4]) == [1, 2, 3]) 17 | 18 | #- mixed args -# 19 | assert(call(f, 1, []) == [1, nil, nil]) 20 | assert(call(f, 1, [2]) == [1, 2, nil]) 21 | assert(call(f, 1, [2, "foo", 4]) == [1, 2, "foo"]) 22 | 23 | #- non terminal list -# 24 | assert(call(f, 1, [2, 3, 4], "foo") == [1, [2, 3, 4], "foo"]) 25 | 26 | #- -# 27 | #- same tests with vararg function -# 28 | #- -# 29 | def g(a, *b) 30 | if size(b) == 0 return [a, nil, nil] 31 | elif size(b) == 1 return [a, b[0], nil] 32 | elif size(b) > 1 return [a, b[0], b[1]] 33 | end 34 | end 35 | 36 | #- simple calls with fixed args -# 37 | assert(call(g) == [nil, nil, nil]) 38 | assert(call(g, 1) == [1, nil, nil]) 39 | assert(call(g, 1, 2) == [1, 2, nil]) 40 | assert(call(g, 1, 2, 3, 4) == [1, 2, 3]) 41 | 42 | #- call with var args -# 43 | assert(call(g, []) == [nil, nil, nil]) 44 | assert(call(g, [1]) == [1, nil, nil]) 45 | assert(call(g, [1, 2]) == [1, 2, nil]) 46 | assert(call(g, [1, 2, 3, 4]) == [1, 2, 3]) 47 | 48 | #- mixed args -# 49 | assert(call(g, 1, []) == [1, nil, nil]) 50 | assert(call(g, 1, [2]) == [1, 2, nil]) 51 | assert(call(g, 1, [2, "foo", 4]) == [1, 2, "foo"]) 52 | 53 | #- non terminal list -# 54 | assert(call(g, 1, [2, 3, 4], "foo") == [1, [2, 3, 4], "foo"]) 55 | 56 | #- -# 57 | #- test with vararg only -# 58 | #- -# 59 | def c(*a) return size(a) end 60 | 61 | #- simple calls with fixed args -# 62 | assert(call(c) == 0) 63 | assert(call(c, 1) == 1) 64 | assert(call(c, 1, 2) == 2) 65 | assert(call(c, 1, 2, 3, 4) == 4) 66 | 67 | #- call with var args -# 68 | assert(call(c, []) == 0) 69 | assert(call(c, [1]) == 1) 70 | assert(call(c, [1, 2]) == 2) 71 | assert(call(c, [1, 2, 3, 4]) == 4) 72 | 73 | #- mixed args -# 74 | assert(call(c, 1, []) == 1) 75 | assert(call(c, 1, [2]) == 2) 76 | assert(call(c, 1, [2, "foo", 4]) == 4) 77 | 78 | #- non terminal list -# 79 | assert(call(c, 1, [2, 3, 4], "foo") == 3) 80 | 81 | #- -# 82 | #- test with native function -# 83 | #- -# 84 | import string 85 | 86 | assert(call(string.format, "a") == "a") 87 | assert(call(string.format, "%i", 1) == "1") 88 | assert(call(string.format, "%i - %i", 1, 2) == "1 - 2") 89 | 90 | assert(call(string.format, "%i - %i", [1, 2]) == "1 - 2") 91 | assert(call(string.format, "%i - %i", [1, 2, 3]) == "1 - 2") 92 | 93 | assert(call(string.format, "%i - %i", 1, [1, 2, 3]) == "1 - 1") 94 | 95 | #- -# 96 | #- try with an insanely high number of arguments to check that we don't blow up the stack -# 97 | #- -# 98 | l50 = [] 99 | for i:1..50 l50.push(i) end 100 | 101 | assert(call(g, l50) == [1, 2, 3]) 102 | assert(call(c, l50) == 50) 103 | 104 | #- call should be working for a class constructor -# 105 | class A 106 | var a 107 | def init(x) 108 | self.a = x 109 | end 110 | end 111 | var inst = call(A, 5) 112 | assert(inst.a == 5) 113 | -------------------------------------------------------------------------------- /src/be_decoder.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_OPCODE_H 9 | #define BE_OPCODE_H 10 | 11 | #define NO_JUMP -1 12 | 13 | /* define field's bit-width */ 14 | #define IOP_BITS 6u 15 | #define IRA_BITS 8u 16 | #define IRKB_BITS 9u 17 | #define IRKC_BITS 9u 18 | 19 | /* define field's bit-width and positions */ 20 | #define IRKC_POS 0u 21 | #define IRKB_POS (IRKC_POS + IRKC_BITS) 22 | #define IRA_POS (IRKB_POS + IRKB_BITS) 23 | #define IOP_POS (IRA_POS + IRA_BITS) 24 | #define IAx_BITS (IRA_BITS + IRKB_BITS + IRKC_BITS) 25 | #define IBx_BITS (IRKC_BITS + IRKB_BITS) 26 | 27 | /* basic field operation */ 28 | #define INS_MASK(pos, bits) ((binstruction)((1 << (bits)) - 1) << (pos)) 29 | #define INS_GETx(i, mask, pos) cast_int(((binstruction)(i) & (mask)) >> (pos)) 30 | #define INS_SETx(v, mask, pos) (((binstruction)(v) << (pos)) & (mask)) 31 | 32 | /* instruction operation */ 33 | #define isK(v) (((v) & (1 << (IRKB_BITS - 1))) != 0) 34 | #define setK(v) ((v) | (1 << (IRKB_BITS - 1))) 35 | #define KR2idx(v) ((v) & 0xFF) 36 | #define isKB(v) (((v) & (1 << (IRA_POS - 1))) != 0) 37 | #define isKC(v) (((v) & (1 << (IRKB_POS - 1))) != 0) 38 | 39 | /* define masks and limit values */ 40 | #define IOP_MASK INS_MASK(IOP_POS, IOP_BITS) 41 | #define IRA_MASK INS_MASK(IRA_POS, IRA_BITS) 42 | #define IRKB_MASK INS_MASK(IRKB_POS, IRKB_BITS) 43 | #define IRKC_MASK INS_MASK(IRKC_POS, IRKC_BITS) 44 | #define IAx_MASK INS_MASK(0, IAx_BITS) 45 | #define IBx_MASK INS_MASK(0, IBx_BITS) 46 | #define IsBx_MAX cast_int(IBx_MASK >> 1) 47 | #define IsBx_MIN cast_int(-IsBx_MAX - 1) 48 | 49 | /* mask for K/R values */ 50 | #define KR_MASK ((1 << (IRKB_BITS-1)) - 1) 51 | 52 | /* get field */ 53 | #define IGET_OP(i) cast(bopcode, INS_GETx(i, IOP_MASK, IOP_POS)) 54 | #define IGET_RA(i) INS_GETx(i, IRA_MASK, IRA_POS) 55 | #define IGET_RKB(i) INS_GETx(i, IRKB_MASK, IRKB_POS) 56 | #define IGET_RKC(i) INS_GETx(i, IRKC_MASK, IRKC_POS) 57 | #define IGET_Bx(i) INS_GETx(i, IBx_MASK, 0) 58 | #define IGET_sBx(i) (IGET_Bx(i) - IsBx_MAX) 59 | 60 | /* set field */ 61 | #define ISET_OP(i) INS_SETx(i, IOP_MASK, IOP_POS) 62 | #define ISET_RA(i) INS_SETx(i, IRA_MASK, IRA_POS) 63 | #define ISET_RKB(i) INS_SETx(i, IRKB_MASK, IRKB_POS) 64 | #define ISET_RKC(i) INS_SETx(i, IRKC_MASK, IRKC_POS) 65 | #define ISET_Bx(i) INS_SETx(i, IBx_MASK, 0) 66 | #define ISET_sBx(i) (ISET_Bx(cast_int(i) + IsBx_MAX)) 67 | 68 | typedef enum { 69 | #define OPCODE(opc) OP_##opc 70 | #include "be_opcodes.h" 71 | #undef OPCODE 72 | } bopcode; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/be_class.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_CLASS_H 9 | #define BE_CLASS_H 10 | 11 | #include "be_object.h" 12 | 13 | #define MT_VARIABLE BE_INDEX 14 | #define MT_METHOD BE_CLOSURE 15 | #define MT_PRIMMETHOD BE_NTVFUNC 16 | 17 | #define be_class_name(cl) ((cl)->name) 18 | #define be_class_members(cl) ((cl)->members) 19 | #define be_class_super(cl) ((cl)->super) 20 | #define be_class_setsuper(self, sup) ((self)->super = (sup)) 21 | #define be_class_setsub(self, sub) ((self)->sub = (sub)) 22 | #define be_instance_name(obj) ((obj)->_class->name) 23 | #define be_instance_class(obj) ((obj)->_class) 24 | #define be_instance_members(obj) ((obj)->members) 25 | #define be_instance_member_count(obj) ((obj)->_class->nvar) 26 | #define be_instance_super(obj) ((obj)->super) 27 | #define be_instance_sub(obj) ((obj)->sub) 28 | 29 | struct bclass { 30 | bcommon_header; 31 | uint16_t nvar; /* members variable data field count */ 32 | struct bclass *super; 33 | bmap *members; 34 | bstring *name; 35 | bgcobject *gray; /* for gc gray list */ 36 | #ifdef __cplusplus 37 | BE_CONSTEXPR bclass(uint16_t nv, bclass *sup, bmap *mem, bstring *s) : 38 | next(0), type(BE_CLASS), marked(GC_CONST), nvar(nv), 39 | super(sup), members(mem), name(s), gray(0) {} 40 | #endif 41 | }; 42 | 43 | struct binstance { 44 | bcommon_header; 45 | struct binstance *super; 46 | struct binstance *sub; 47 | bclass *_class; 48 | bgcobject *gray; /* for gc gray list */ 49 | bvalue members[1]; /* members variable data field */ 50 | }; 51 | 52 | /* special structure accepting 3 instance variables used only for bytes() solidification */ 53 | struct binstance_arg3 { 54 | bcommon_header; 55 | struct binstance *super; 56 | struct binstance *sub; 57 | bclass *_class; 58 | bgcobject *gray; /* for gc gray list */ 59 | bvalue members[3]; /* members variable data field */ 60 | }; 61 | 62 | bclass* be_newclass(bvm *vm, bstring *name, bclass *super); 63 | void be_class_compress(bvm *vm, bclass *c); 64 | int be_class_attribute(bvm *vm, bclass *c, bstring *attr); 65 | void be_class_member_bind(bvm *vm, bclass *c, bstring *name, bbool var); 66 | void be_class_method_bind(bvm *vm, bclass *c, bstring *name, bproto *p, bbool is_static); 67 | void be_class_native_method_bind(bvm *vm, bclass *c, bstring *name, bntvfunc f); 68 | void be_class_closure_method_bind(bvm *vm, bclass *c, bstring *name, bclosure *cl); 69 | int be_class_member(bvm *vm, bclass *obj, bstring *name, bvalue *dst); 70 | bbool be_class_setmember(bvm *vm, bclass *obj, bstring *name, bvalue *src); 71 | int be_class_closure_count(bclass *c); 72 | void be_class_upvalue_init(bvm *vm, bclass *c); 73 | bbool be_class_newobj(bvm *vm, bclass *c, int pos, int argc, int mode); 74 | int be_instance_member_simple(bvm *vm, binstance *obj, bstring *name, bvalue *dst); 75 | int be_instance_member(bvm *vm, binstance *obj, bstring *name, bvalue *dst); 76 | bbool be_instance_setmember(bvm *vm, binstance *obj, bstring *name, bvalue *src); 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/be_gc.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_GC_H 9 | #define BE_GC_H 10 | 11 | #include "be_object.h" 12 | 13 | #define gc_object(o) cast(bgcobject*, o) 14 | #define gc_cast(o, t, T) ((o) && (o)->type == (t) ? (T*)(o) : NULL) 15 | #define cast_proto(o) gc_cast(o, BE_PROTO, bproto) 16 | #define cast_closure(o) gc_cast(o, BE_CLOSURE, bclosure) 17 | #define cast_ntvclos(o) gc_cast(o, BE_NTVCLOS, bntvclos) 18 | #define cast_str(o) gc_cast(o, BE_STRING, bstring) 19 | #define cast_class(o) gc_cast(o, BE_CLASS, bclass) 20 | #define cast_instance(o) gc_cast(o, BE_INSTANCE, binstance) 21 | #define cast_map(o) gc_cast(o, BE_MAP, bmap) 22 | #define cast_list(o) gc_cast(o, BE_LIST, blist) 23 | #define cast_module(o) gc_cast(o, BE_MODULE, bmodule) 24 | 25 | #define gc_ismark(o, m) (((o)->marked & 0x03) == m) 26 | #define gc_iswhite(o) gc_ismark((o), GC_WHITE) 27 | #define gc_isgray(o) gc_ismark((o), GC_GRAY) 28 | #define gc_isdark(o) gc_ismark((o), GC_DARK) 29 | 30 | #define gc_setmark(o, m) \ 31 | if (!gc_isconst(o)) { \ 32 | (o)->marked &= ~0x03; \ 33 | (o)->marked |= (m) & 0x03; \ 34 | } 35 | 36 | #define gc_setwhite(o) gc_setmark((o), GC_WHITE) 37 | #define gc_setgray(o) gc_setmark((o), GC_GRAY) 38 | #if BE_USE_PERF_COUNTERS 39 | #define gc_setdark(o) { vm->counter_gc_kept++; gc_setmark((o), GC_DARK); } 40 | #else 41 | #define gc_setdark(o) gc_setmark((o), GC_DARK) 42 | #endif 43 | #define gc_isfixed(o) (((o)->marked & GC_FIXED) != 0) 44 | #define gc_setfixed(o) ((o)->marked |= GC_FIXED) 45 | #define gc_clearfixed(o) ((o)->marked &= ~GC_FIXED) 46 | #define gc_isconst(o) (((o)->marked & GC_CONST) != 0) 47 | #define gc_exmark(o) (((o)->marked >> 4) & 0x0F) 48 | #define gc_setexmark(o, k) ((o)->marked |= (k) << 4) 49 | 50 | #define be_isgcobj(o) (var_primetype(o) >= BE_GCOBJECT && var_primetype(o) < BE_GCOBJECT_MAX) 51 | #define be_gcnew(v, t, s) be_newgcobj((v), (t), sizeof(s)) 52 | 53 | #define set_fixed(s) bbool _was_fixed = be_gc_fix_set(vm, cast(bgcobject*, (s)), 1) 54 | #define restore_fixed(s) be_gc_fix_set(vm, cast(bgcobject*, (s)), _was_fixed); 55 | 56 | /* the GC mark uses bit4:0 of the `object->marked` field, 57 | * so other bits can be used for special flags (ex-mark). */ 58 | typedef enum { 59 | GC_WHITE = 0x00, /* unreachable object */ 60 | GC_GRAY = 0x01, /* unscanned object */ 61 | GC_DARK = 0x02, /* scanned object */ 62 | GC_FIXED = 0x04, /* disable collection mark */ 63 | GC_CONST = 0x08 /* constant object mark */ 64 | } bgcmark; 65 | 66 | void be_gc_init(bvm *vm); 67 | void be_gc_deleteall(bvm *vm); 68 | void be_gc_setsteprate(bvm *vm, int rate); 69 | void be_gc_setpause(bvm *vm, int pause); 70 | size_t be_gc_memcount(bvm *vm); 71 | bgcobject *be_newgcobj(bvm *vm, int type, size_t size); 72 | bgcobject* be_gc_newstr(bvm *vm, size_t size, int islong); 73 | void be_gc_fix(bvm *vm, bgcobject *obj); 74 | void be_gc_unfix(bvm *vm, bgcobject *obj); 75 | bbool be_gc_fix_set(bvm *vm, bgcobject *obj, bbool fix); 76 | void be_gc_collect(bvm *vm); 77 | void be_gc_auto(bvm *vm); 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/be_opcodes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | /* define opcode, don't change order */ 9 | /* opcode parameters description */ 10 | OPCODE(ADD), /* A, B, C | R(A) <- RK(B) + RK(C) */ 11 | OPCODE(SUB), /* A, B, C | R(A) <- RK(B) - RK(C) */ 12 | OPCODE(MUL), /* A, B, C | R(A) <- RK(B) * RK(C) */ 13 | OPCODE(DIV), /* A, B, C | R(A) <- RK(B) / RK(C) */ 14 | OPCODE(MOD), /* A, B, C | R(A) <- RK(B) % RK(C) */ 15 | OPCODE(LT), /* A, B, C | R(A) <- RK(B) < RK(C) */ 16 | OPCODE(LE), /* A, B, C | R(A) <- RK(B) <= RK(C) */ 17 | OPCODE(EQ), /* A, B, C | R(A) <- RK(B) == RK(C) */ 18 | OPCODE(NE), /* A, B, C | R(A) <- RK(B) != RK(C) */ 19 | OPCODE(GT), /* A, B, C | R(A) <- RK(B) > RK(C) */ 20 | OPCODE(GE), /* A, B, C | R(A) <- RK(B) >= RK(C) */ 21 | OPCODE(AND), /* A, B, C | R(A) <- RK(B) & RK(C) */ 22 | OPCODE(OR), /* A, B, C | R(A) <- RK(B) | RK(C) */ 23 | OPCODE(XOR), /* A, B, C | R(A) <- RK(B) ^ RK(C) */ 24 | OPCODE(SHL), /* A, B, C | R(A) <- RK(B) << RK(C) */ 25 | OPCODE(SHR), /* A, B, C | R(A) <- RK(B) >> RK(C) */ 26 | OPCODE(CONNECT), /* A, B, C | R(A) <- connect(RK(B), RK(C)) */ 27 | OPCODE(NEG), /* A, B | R(A) <- -RK(B) */ 28 | OPCODE(FLIP), /* A, B | R(A) <- ~RK(B) */ 29 | OPCODE(LDNIL), /* A | R(A) <- nil */ 30 | OPCODE(LDBOOL), /* A, B, C | R(A) <- cast_bool(B), if(C): pc++ */ 31 | OPCODE(LDINT), /* A, sBx | R(A) <- sBx */ 32 | OPCODE(LDCONST), /* A, Bx | R(A) <- K(Bx) */ 33 | OPCODE(MOVE), /* A, B, C | R(A) <- RK(B) */ 34 | OPCODE(GETGBL), /* A, Bx | R(A) <- GLOBAL(Bx) */ 35 | OPCODE(SETGBL), /* A, Bx | R(A) -> GLOBAL(Bx) */ 36 | OPCODE(GETUPV), /* A, Bx | R(A) <- UPVALUE(Bx)*/ 37 | OPCODE(SETUPV), /* A, Bx | R(A) -> UPVALUE(Bx)*/ 38 | OPCODE(JMP), /* sBx | pc <- pc + sBx */ 39 | OPCODE(JMPT), /* A, sBx | if(R(A)): pc <- pc + sBx */ 40 | OPCODE(JMPF), /* A, sBx | if(not R(A)): pc <- pc + sBx */ 41 | OPCODE(CALL), /* A, B | CALL(R(A), B) */ 42 | OPCODE(RET), /* A, B | if (R(A)) R(-1) <- RK(B) else R(-1) <- nil */ 43 | OPCODE(CLOSURE), /* A, Bx | R(A) <- CLOSURE(proto_table[Bx])*/ 44 | OPCODE(GETMBR), /* A, B, C | R(A) <- RK(B).RK(C) */ 45 | OPCODE(GETMET), /* A, B, C | R(A) <- RK(B).RK(C), R(A+1) <- RK(B) */ 46 | OPCODE(SETMBR), /* A, B, C | R(A).RK(B) <- RK(C) */ 47 | OPCODE(GETIDX), /* A, B, C | R(A) <- RK(B)[RK(C)] */ 48 | OPCODE(SETIDX), /* A, B, C | R(A)[RK(B)] <- RK(C) */ 49 | OPCODE(SETSUPER), /* A, B | class:R(A) set super with class:RK(B) */ 50 | OPCODE(CLOSE), /* A | close upvalues */ 51 | OPCODE(IMPORT), /* A, B, C | IF (A == C) import module name as RK(B) to RK(A), ELSE from module RK(C) import name as RK(B) to RK(A) */ 52 | OPCODE(EXBLK), /* A, Bx | ... */ 53 | OPCODE(CATCH), /* A, B, C | ... */ 54 | OPCODE(RAISE), /* A, B, C | RAISE(B,C) B is code, C is description. A==0 only B provided, A==1 B and C are provided, A==2 rethrow with both parameters already on stack */ 55 | OPCODE(CLASS), /* Bx | init class in K[Bx] */ 56 | OPCODE(GETNGBL), /* A, B | R(A) <- GLOBAL[RK(B)] by name */ 57 | OPCODE(SETNGBL) /* A, B | R(A) -> GLOBAL[RK(B)] by name */ 58 | -------------------------------------------------------------------------------- /tools/highlighters/Pygments/berry.py: -------------------------------------------------------------------------------- 1 | import re 2 | from pygments.lexer import RegexLexer, words, default, include, bygroups 3 | from pygments.token import Text, Comment, Whitespace, Operator, Keyword, Name, String, Number, Punctuation 4 | 5 | __all__ = ['BerryLexer'] 6 | 7 | line_re = re.compile('.*?\n') 8 | 9 | class BerryLexer(RegexLexer): 10 | 11 | # For Berry source code (version v0.1.0) 12 | # Contributed by Shaun Brown (Beormund) 13 | 14 | name = 'Berry' 15 | aliases = ['berry', 'be'] 16 | filenames = ['*.be'] 17 | mimetypes = ['text/x-berry', 'application/x-berry'] 18 | 19 | _name = r'\b[^\W\d]\w*' 20 | 21 | # (def)\b((?:\s)*) 22 | # (def)\b(\s*$)(?/-]', Operator), 38 | (r'[(){}\[\],.;]', Punctuation), 39 | include('controls'), 40 | include('builtins'), 41 | include('funccall'), 42 | include('member'), 43 | include('name'), 44 | include('strings') 45 | ], 46 | 'whitespace': [ 47 | (r'\s+', Whitespace), 48 | (r'#-(.|\n)*?-#', Comment.Multiline), 49 | (r'#(\n|[\w\W]*?\n)', Comment.Single) 50 | ], 51 | 'keywords': [ 52 | (words(( 53 | 'as', 'break', 'continue', 'import', 'static', 'self', 'super'), 54 | suffix=r'\b'), Keyword.Reserved), 55 | (r'(true|false|nil)\b', Keyword.Constant), 56 | (r'(var|def)\b', Keyword.Declaration) 57 | ], 58 | 'controls': [ 59 | (words(( 60 | 'if', 'elif', 'else', 'for', 'while', 'do', 'end', 'break', 61 | 'continue', 'return', 'try', 'except', 'raise'), 62 | suffix=r'\b'), Operator.Word) 63 | ], 64 | 'builtins': [ 65 | (words(( 66 | 'assert', 'bool', 'input', 'classname', 'classof', 'number', 'real', 67 | 'bytes', 'compile', 'map', 'list', 'int', 'isinstance', 'print', 68 | 'range', 'str', 'super', 'module', 'size', 'issubclass', 'open', 69 | 'file', 'type', 'call'), 70 | suffix=r'\b'), Name.Builtin) 71 | ], 72 | 'numbers': [ 73 | (r'0[xX](?:_?[a-fA-F0-9])+', Number.Hex), 74 | (r'[-]?\d+', Number.Integer), 75 | (r'([-]?\d*\.?\d+)(?:[eE]([-+]?\d+))?', Number.Float) 76 | ], 77 | 'name': [ 78 | (_name, Name) 79 | ], 80 | 'funcname': [ 81 | (_name, Name.Function, '#pop') 82 | ], 83 | 'funccall': [ 84 | ( _name + r'(?=\s*\()', Name.Function, '#pop') 85 | ], 86 | 'member': [ 87 | ( r'(?<=\.)' + _name + r'\b(?!\()', Name.Attribute, '#pop') 88 | ], 89 | 'classname': [ 90 | (_name, Name.Class, '#pop'), 91 | ], 92 | 'import': [ 93 | (r'(\s+)(as)(\s+)', bygroups(Text, Keyword, Text)), 94 | ], 95 | 'strings': [ 96 | (r'"(?:\\?.)*?"', String.Double, '#pop'), 97 | (r'\'(?:\\?.)*?\'', String.Single, '#pop') 98 | ] 99 | } 100 | -------------------------------------------------------------------------------- /src/be_repl.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "berry.h" 9 | #include "be_repl.h" 10 | #include 11 | 12 | #define safecall(func, ...) if (func) { func(__VA_ARGS__); } 13 | 14 | #if BE_USE_SCRIPT_COMPILER 15 | 16 | static int try_return(bvm *vm, const char *line) 17 | { 18 | int res, idx; 19 | line = be_pushfstring(vm, "return (%s)", line); 20 | idx = be_absindex(vm, -1); /* get the source text absolute index */ 21 | res = be_loadbuffer(vm, "stdin", line, strlen(line)); /* compile line */ 22 | be_remove(vm, idx); /* remove source string */ 23 | return res; 24 | } 25 | 26 | static bbool is_multline(bvm *vm) 27 | { 28 | const char *msg = be_tostring(vm, -1); 29 | size_t len = strlen(msg); 30 | if (len > 5) { /* multi-line text if the error message is 'EOS' at the end */ 31 | return !strcmp(msg + len - 5, "'EOS'"); 32 | } 33 | return bfalse; 34 | } 35 | 36 | static int compile(bvm *vm, char *line, breadline getl, bfreeline freel) 37 | { 38 | int res = try_return(vm, line); 39 | if (be_getexcept(vm, res) == BE_SYNTAX_ERROR) { 40 | be_pop(vm, 2); /* pop exception values */ 41 | be_pushstring(vm, line); 42 | safecall(freel, line); /* free line buffer */ 43 | for (;;) { 44 | const char *src = be_tostring(vm, -1); /* get source code */ 45 | int idx = be_absindex(vm, -1); /* get the source text absolute index */ 46 | /* compile source line */ 47 | res = be_loadbuffer(vm, "stdin", src, strlen(src)); 48 | if (!res || !is_multline(vm)) { 49 | be_remove(vm, idx); /* remove source code */ 50 | return res; 51 | } 52 | be_pop(vm, 2); /* pop exception values */ 53 | line = getl(">> "); /* read a new input line */ 54 | be_pushfstring(vm, "\n%s", line); 55 | safecall(freel, line); /* free line buffer */ 56 | be_strconcat(vm, -2); 57 | be_pop(vm, 1); /* pop new line */ 58 | } 59 | } else { 60 | safecall(freel, line); /* free line buffer */ 61 | } 62 | return res; 63 | } 64 | 65 | static int call_script(bvm *vm) 66 | { 67 | int res = be_pcall(vm, 0); /* call the main function */ 68 | switch (res) { 69 | case BE_OK: /* execution succeed */ 70 | if (!be_isnil(vm, -1)) { /* print return value when it's not nil */ 71 | be_dumpvalue(vm, -1); 72 | } 73 | be_pop(vm, 1); /* pop the result value */ 74 | break; 75 | case BE_EXCEPTION: /* vm run error */ 76 | be_dumpexcept(vm); 77 | be_pop(vm, 1); /* pop the function value */ 78 | break; 79 | default: /* BE_EXIT or BE_MALLOC_FAIL */ 80 | return res; 81 | } 82 | return 0; 83 | } 84 | 85 | BERRY_API int be_repl(bvm *vm, breadline getline, bfreeline freeline) 86 | { 87 | char *line; 88 | be_assert(getline != NULL); 89 | while ((line = getline("> ")) != NULL) { 90 | int res = compile(vm, line, getline, freeline); 91 | if (res == BE_MALLOC_FAIL) 92 | return BE_MALLOC_FAIL; 93 | if (res) { 94 | be_dumpexcept(vm); 95 | } else { /* compiled successfully */ 96 | res = call_script(vm); 97 | if (res) { 98 | return res == BE_EXIT ? be_toindex(vm, -1) : res; 99 | } 100 | } 101 | } 102 | be_writenewline(); 103 | return 0; 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /src/be_globallib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2021 Guan Wenliang & Stephan Hadinger 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | #include "be_module.h" 10 | #include "be_string.h" 11 | #include "be_vector.h" 12 | #include "be_class.h" 13 | #include "be_debug.h" 14 | #include "be_map.h" 15 | #include "be_vm.h" 16 | #include "be_var.h" 17 | #include 18 | 19 | #if BE_USE_GLOBAL_MODULE 20 | 21 | #define global(vm) ((vm)->gbldesc.global) 22 | 23 | static void dump_map_keys(bvm *vm, bmap *map) 24 | { 25 | if (!map) { return; } /* protect agains potential null pointer */ 26 | bmapnode *node; 27 | bmapiter iter = be_map_iter(); 28 | while ((node = be_map_next(map, &iter)) != NULL) { 29 | if (var_isstr(&node->key)) { 30 | /* check if the global was not undefined/removed */ 31 | int idx = var_toidx(&node->value); 32 | if (idx >= 0) { /* the key is present in global, and the index is valid */ 33 | bstring *s = var_tostr(&node->key); 34 | be_pushstring(vm, str(s)); 35 | be_data_push(vm, -2); 36 | be_pop(vm, 1); 37 | } 38 | } 39 | } 40 | } 41 | 42 | static int m_globals(bvm *vm) 43 | { 44 | be_newobject(vm, "list"); 45 | dump_map_keys(vm, global(vm).vtab); 46 | be_pop(vm, 1); 47 | be_return(vm); 48 | } 49 | 50 | static int m_contains(bvm *vm) 51 | { 52 | int top = be_top(vm); 53 | if (top >= 1 && be_isstring(vm, 1)) { 54 | const char * name = be_tostring(vm, 1); 55 | int idx = be_global_find(vm, be_newstr(vm, name)); 56 | be_pushbool(vm, idx >= 0); 57 | be_return(vm); 58 | } 59 | be_return_nil(vm); 60 | } 61 | 62 | static int m_findglobal(bvm *vm) 63 | { 64 | int top = be_top(vm); 65 | if (top >= 1 && be_isstring(vm, 1)) { 66 | const char * name = be_tostring(vm, 1); 67 | be_getglobal(vm, name); 68 | be_return(vm); 69 | } 70 | be_return_nil(vm); 71 | } 72 | 73 | static int m_setglobal(bvm *vm) 74 | { 75 | int top = be_top(vm); 76 | if (top >= 2 && be_isstring(vm, 1)) { 77 | const char * name = be_tostring(vm, 1); 78 | be_setglobal(vm, name); 79 | } 80 | be_return_nil(vm); 81 | } 82 | 83 | /* Remove a global variable from global scope */ 84 | /* Internally the global name cannot be removed but it's value is replaced with BE_NONE */ 85 | /* and global function pretend that BE_NONE is equivalent to the name being absent */ 86 | static int m_undef(bvm *vm) 87 | { 88 | int top = be_top(vm); 89 | if (top >= 1 && be_isstring(vm, 1)) { 90 | be_global_undef(vm, be_newstr(vm, be_tostring(vm, 1))); 91 | } 92 | be_return_nil(vm); 93 | } 94 | 95 | #if !BE_USE_PRECOMPILED_OBJECT 96 | be_native_module_attr_table(global) { 97 | be_native_module_function("()", m_globals), 98 | be_native_module_function("contains", m_contains), 99 | be_native_module_function("member", m_findglobal), 100 | be_native_module_function("setmember", m_setglobal), 101 | be_native_module_function("undef", m_undef), 102 | }; 103 | 104 | be_define_native_module(global, NULL); 105 | #else 106 | /* @const_object_info_begin 107 | module global (scope: global, depend: BE_USE_GLOBAL_MODULE) { 108 | (), func(m_globals) 109 | contains, func(m_contains) 110 | member, func(m_findglobal) 111 | setmember, func(m_setglobal) 112 | undef, func(m_undef) 113 | } 114 | @const_object_info_end */ 115 | #include "../generate/be_fixed_global.h" 116 | #endif 117 | 118 | #endif /* BE_USE_GLOBAL_MODULE */ 119 | -------------------------------------------------------------------------------- /tests/class_static.be: -------------------------------------------------------------------------------- 1 | def assert_attribute_error(f) 2 | try 3 | f() 4 | assert(false, 'unexpected execution flow') 5 | except .. as e, m 6 | assert(e == 'attribute_error') 7 | end 8 | end 9 | 10 | class A 11 | static a #- deprecated syntax -# 12 | def init() self.b = 2 end 13 | def f() end 14 | var b 15 | static var c, s, r #- preferred syntax -# 16 | end 17 | 18 | assert(A.a == nil) 19 | assert(A.c == nil) 20 | assert(A.s == nil) 21 | assert_attribute_error(/-> A.b) 22 | assert_attribute_error(/-> A.d) 23 | 24 | a = A() 25 | assert(a.b == 2) 26 | assert(a.a == nil) 27 | assert(a.c == nil) 28 | 29 | A.a = 1 30 | A.c = 3 31 | A.s = "foo" 32 | A.r = 3.5 33 | assert(a.a == 1) 34 | assert(a.c == 3) 35 | assert(A.a == 1) 36 | assert(A.c == 3) 37 | import gc gc.collect() 38 | assert(A.s == "foo") 39 | assert(a.s == "foo") 40 | assert(A.r == 3.5) 41 | assert(a.r == 3.5) 42 | 43 | #- test valid or invalid methods and members -# 44 | 45 | def assert_attribute_error(c) 46 | try 47 | compile(c)() 48 | assert(false, 'unexpected execution flow') 49 | except .. as e, m 50 | assert(e == 'attribute_error') 51 | end 52 | end 53 | 54 | class A 55 | var a, g 56 | static h 57 | def init() self.a = 1 end 58 | def f(x, y) return type(self) end 59 | end 60 | a=A() 61 | a.g = def (x, y) return type(x) end 62 | A.h = def (x, y) return type(x) end 63 | 64 | assert(type(a.g) == 'function') 65 | assert(type(a.h) == 'function') 66 | 67 | assert(a.g(1) == 'int') 68 | assert(a.h(1) == 'int') 69 | assert(A.h(1) == 'int') 70 | 71 | 72 | class A 73 | var a 74 | static def g(x, y) return [x,y] end 75 | static h = def (x, y) return [x,y] end 76 | def init() self.a = 1 end 77 | def f(x, y) return type(self) end 78 | end 79 | a=A() 80 | assert(type(a.g) == 'function') 81 | assert(type(a.h) == 'function') 82 | assert(type(A.g) == 'function') 83 | assert(type(A.h) == 'function') 84 | assert(a.g(1,2) == [1,2]) 85 | assert(a.h(1,2) == [1,2]) 86 | assert(A.g(1,2) == [1,2]) 87 | assert(A.h(1,2) == [1,2]) 88 | a.a = def (x,y) return [x,y] end 89 | assert(a.a(1,2) == [1,2]) 90 | 91 | 92 | #- test static initializers -# 93 | class A 94 | static a = 1, b, c = 3.5, d = 42, e = "foo", f = [1], g = {} 95 | var aa,ab 96 | end 97 | 98 | assert(A.a == 1) 99 | assert(A.b == nil) 100 | assert(A.c == 3.5) 101 | assert(A.d == 42) 102 | assert(A.e == "foo") 103 | assert(A.f == [1]) 104 | 105 | a = A() 106 | assert(a.a == 1) 107 | assert(a.b == nil) 108 | assert(a.c == 3.5) 109 | assert(a.d == 42) 110 | assert(a.e == "foo") 111 | assert(a.f == [1]) 112 | assert(a.g == A.g) 113 | assert(a.aa == nil) 114 | assert(a.ab == nil) 115 | 116 | #- used to fail for subclasses -# 117 | class A static a=1 end 118 | class B:A static a=A def f() end static b=1 static c=A end 119 | assert(A.a == 1) 120 | assert(B.a == A) 121 | assert(B.b == 1) 122 | assert(B.c == A) 123 | 124 | #- static class get an implicit `_class` variable -# 125 | class A 126 | static def f(x) return _class end 127 | end 128 | assert(A.f() == A) 129 | 130 | #- static class within a class -# 131 | class A 132 | static class B 133 | static def f() return 1 end 134 | def g() return 2 end 135 | end 136 | end 137 | a = A() 138 | b = A.B() 139 | assert(classname(a) == 'A') 140 | assert(classname(b) == 'B') 141 | assert(A.B.f() == 1) 142 | assert(b.g() == 2) 143 | assert(super(A.B) == nil) 144 | 145 | #- `_class` initializer can now be used in initializer code -# 146 | class A 147 | static var a = 1 148 | static var b = _class 149 | static var c = [_class.a, _class.b] 150 | static def f(x) 151 | return _class 152 | end 153 | end 154 | assert(A.a == 1) 155 | assert(A.b == A) 156 | assert(A.c == [1, A]) 157 | assert(A.f(1) == A) 158 | 159 | # bug when superclass is a member 160 | # the following would break with: 161 | # 162 | # attribute_error: class 'B' cannot assign to static attribute 'aa' 163 | # stack traceback: 164 | # stdin:1: in function `main` 165 | class B end m = module('m') m.B = B 166 | class A : m.B static aa = 1 end 167 | -------------------------------------------------------------------------------- /tests/list.be: -------------------------------------------------------------------------------- 1 | l = [1, 2, 3, 4, 5] 2 | assert(l[0] == 1) 3 | assert(l[1] == 2) 4 | assert(l[2] == 3) 5 | assert(l[3] == 4) 6 | assert(l[4] == 5) 7 | assert(str(l) == '[1, 2, 3, 4, 5]') 8 | 9 | it = l.iter() 10 | assert(it() == 1) 11 | assert(it() == 2) 12 | assert(it() == 3) 13 | assert(it() == 4) 14 | assert(it() == 5) 15 | 16 | l.insert(0, 10) 17 | assert(l[0] == 10) 18 | assert(l.size() == 6) 19 | l.remove(0) 20 | assert(l.size() == 5) 21 | assert(l[0] == 1) 22 | l.setitem(0, 42) 23 | assert(l[0] == 42) 24 | assert(l.item(2) == 3) 25 | l.resize(10) 26 | assert(l.size() == 10) 27 | assert(l.tostring() == '[42, 2, 3, 4, 5, nil, nil, nil, nil, nil]') 28 | 29 | assert(([] == []) == true) 30 | assert(([] != []) == false) 31 | assert(([1] == [1]) == true) 32 | assert(([1] != [1]) == false) 33 | assert(([1] == [0]) == false) 34 | assert(([1] != [0]) == true) 35 | assert(([1, 2, 3] == [1, 2, 3]) == true) 36 | assert(([1, 2, 3] != [1, 2, 3]) == false) 37 | assert(([1, 2, 3] == [1, 2, 4]) == false) 38 | assert(([1, 2, 3] != [1, 2, 4]) == true) 39 | assert(([1, 2, ['w']] == [1, 2, ['w']]) == true) 40 | assert(([1, 2, ['w']] != [1, 2, ['w']]) == false) 41 | assert(([1, 2, ['w']] == [1, 2, ['z']]) == false) 42 | assert(([1, 2, ['w']] != [1, 2, ['z']]) == true) 43 | assert(([1, 2, ['w']] == [1, 2, []]) == false) 44 | assert(([1, 2, ['w']] != [1, 2, []]) == true) 45 | 46 | var l = [0, 1, 2, 3] 47 | assert(l[-1] == 3) 48 | assert(l[-2] == 2) 49 | var t = l.copy() 50 | l.insert(-2, 4) 51 | assert(t == [0, 1, 2, 3] && t != l) 52 | assert(l == [0, 1, 4, 2, 3]) 53 | l.remove(-2) 54 | assert(l == [0, 1, 4, 3]) 55 | assert(l.reverse() == [3, 4, 1, 0]) 56 | assert(l + [5, 6] == [3, 4, 1, 0, 5, 6]) 57 | l = [0] 58 | assert(l .. '3' == [0, '3']) 59 | l.push(1) 60 | assert(l == [0, '3', 1]) 61 | assert(l.concat() == '031') 62 | l.pop() 63 | assert(l == [0, '3']) 64 | l.pop(0) 65 | assert(l == ['3']) 66 | 67 | l1 = [0, 1] 68 | l2 = [2, 3] 69 | assert(l1+l2==[0, 1, 2, 3]) 70 | assert(l1 == [0, 1]) 71 | assert(l2 == [2, 3]) 72 | assert(l1+[2] == [0, 1, 2]) 73 | assert([-1]+l1 == [-1, 0, 1]) 74 | assert(l1 == [0, 1]) 75 | 76 | #- find -# 77 | #- if no argument return nil -# 78 | assert([].find() == nil) 79 | assert([1,2].find() == nil) 80 | assert([1,1,nil,2].find() == nil) 81 | 82 | #- nil if not found -# 83 | assert([1,2].find(3) == nil) 84 | assert([1,2].find(true) == nil) 85 | assert([1,2].find('foo') == nil) 86 | 87 | #- if found -# 88 | assert([1,2,3,4].find(1) == 0) 89 | assert([1,2,3,4].find(2) == 1) 90 | assert([1,2,3,4].find(3) == 2) 91 | assert([1,2,3,4].find(4) == 3) 92 | assert([1,2,"foo",4].find('foo') == 2) 93 | 94 | #- if multiple occurrences -# 95 | assert([1,1,2,2].find(1) == 0) 96 | assert([1,1,2,2].find(2) == 2) 97 | 98 | #- look for nil -# 99 | assert([1,1,nil,2].find(nil) == 2) 100 | 101 | #- sub-structure -# 102 | assert([1,[1,nil,2],3,[3]].find(3) == 2) 103 | assert([1,[1,nil,2],3,[3]].find([3]) == 3) 104 | assert([1,[1,nil,2],3,[3]].find([1,nil,2]) == 1) 105 | 106 | #- keys() -# 107 | assert(str(["a",'b',0].keys()) == "(0..2)") 108 | assert(str([nil].keys()) == "(0..0)") 109 | assert(str([].keys()) == "(0..-1)") 110 | 111 | #- concat with delimiter -# 112 | assert(["foo","bar",0].concat() == "foobar0") 113 | assert([1,2,3].concat() == "123") 114 | assert(["foo","bar",0].concat('') == "foobar0") 115 | assert([1,2,3].concat('') == "123") 116 | 117 | assert(["foo","bar",0].concat('-') == "foo-bar-0") 118 | assert([].concat('<->') == "") 119 | assert(["foo"].concat('<->') == "foo") 120 | assert(["foo","bar",0].concat('<->') == "foo<->bar<->0") 121 | 122 | assert(["","foo","bar",0].concat('<->') == "<->foo<->bar<->0") 123 | assert(["","",1,"bar",0].concat('<->') == "<-><->1<->bar<->0") 124 | assert(["","",1,"bar",0].concat('') == "1bar0") 125 | 126 | assert([1,2,3].concat('-') == "1-2-3") 127 | assert([1,"2",3].concat('-') == "1-2-3") 128 | 129 | assert(["",2,3].concat('-') == "-2-3") 130 | 131 | #- negative indices -# 132 | assert([1,2,3,4][0] == 1) 133 | assert([1,2,3,4][-1] == 4) 134 | assert([1,2,3,4][-2] == 3) 135 | 136 | assert([1,2,3,4][1..10] == [2,3,4]) 137 | assert([1,2,3,4][1..-1] == [2,3,4]) 138 | assert([1,2,3,4][1..-2] == [2,3]) 139 | assert([1,2,3,4][3..2] == []) 140 | assert([1,2,3,4][2..-3] == []) 141 | 142 | #- set negative index -# 143 | l = [1, 2] 144 | l[-1] = 3 145 | assert(l == [1,3]) 146 | l[-1] += 1 147 | assert(l == [1,4]) 148 | l[-2] += l[-1] 149 | assert(l == [5,4]) 150 | -------------------------------------------------------------------------------- /src/be_lexer.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #ifndef BE_LEXER_H 9 | #define BE_LEXER_H 10 | 11 | #include "be_object.h" 12 | 13 | typedef enum { 14 | TokenNone = 0, 15 | TokenEOS, /* end of source */ 16 | TokenId, /* identifier */ 17 | TokenInteger, 18 | TokenReal, 19 | TokenString, 20 | /* operator, don't change order */ 21 | /* assign operator */ 22 | OptAssign, /* operator, = */ 23 | OptAddAssign, /* operator, += */ 24 | OptSubAssign, /* operator, -= */ 25 | OptMulAssign, /* operator, *= */ 26 | OptDivAssign, /* operator, /= */ 27 | OptModAssign, /* operator, %= */ 28 | OptAndAssign, /* operator, &= */ 29 | OptOrAssign, /* operator, |= */ 30 | OptXorAssign, /* operator, ^= */ 31 | OptLsfAssign, /* operator, <<= */ 32 | OptRsfAssign, /* operator, >>= */ 33 | /* binary operator */ 34 | OptAdd, /* operator, + */ 35 | OptSub, /* operator, - */ 36 | OptMul, /* operator, * */ 37 | OptDiv, /* operator, / */ 38 | OptMod, /* operator, % */ 39 | OptLT, /* operator, < */ 40 | OptLE, /* operator, <= */ 41 | OptEQ, /* operator, == */ 42 | OptNE, /* operator, != */ 43 | OptGT, /* operator, > */ 44 | OptGE, /* operator, >= */ 45 | OptBitAnd, /* operatoe, & */ 46 | OptBitOr, /* operatoe, | */ 47 | OptBitXor, /* operatoe, ^ */ 48 | OptShiftL, /* operatoe, << */ 49 | OptShiftR, /* operatoe, >> */ 50 | OptConnect, /* operator, .. */ 51 | OptAnd, /* operator, && */ 52 | OptOr, /* operator, || */ 53 | /* unary operator */ 54 | OptNot, /* operator, ! */ 55 | OptFlip, /* operator, ~ */ 56 | /* postfix operator or bracket */ 57 | OptSpaceLBK, /* operator, ( bracket (with space/newline before) */ 58 | OptCallLBK, /* operator, ( bracket (call - no space before) */ 59 | OptRBK, /* operator, ) bracket */ 60 | OptLSB, /* operator, [ square bracket */ 61 | OptRSB, /* operator, ] square bracket */ 62 | OptLBR, /* operator, { brace */ 63 | OptRBR, /* operator, } brace */ 64 | OptDot, /* operator, . dot */ 65 | /* other symbol */ 66 | OptComma, /* operator, , */ 67 | OptSemic, /* operator, ; */ 68 | OptColon, /* operator, : */ 69 | OptQuestion, /* operator, ? */ 70 | OptArrow, /* operator, -> */ 71 | OptWalrus, /* operator, := */ 72 | /* keyword */ 73 | KeyIf, /* keyword if */ 74 | KeyElif, /* keyword elif */ 75 | KeyElse, /* keyword else */ 76 | KeyWhile, /* keyword while */ 77 | KeyFor, /* keyword for */ 78 | KeyDef, /* keyword def */ 79 | KeyEnd, /* keyword end */ 80 | KeyClass, /* keyword class */ 81 | KeyBreak, /* keyword break */ 82 | KeyContinue, /* keyword continue */ 83 | KeyReturn, /* keyword return */ 84 | KeyTrue, /* keyword true */ 85 | KeyFalse, /* keyword false */ 86 | KeyNil, /* keyword nil */ 87 | KeyVar, /* keyword var */ 88 | KeyDo, /* keyword do */ 89 | KeyImport, /* keyword import */ 90 | KeyAs, /* keyword as */ 91 | KeyTry, /* keyword try */ 92 | KeyExcept, /* keyword except */ 93 | KeyRaise, /* keyword raise */ 94 | KeyStatic, /* keyword static */ 95 | } btokentype; 96 | 97 | struct blexerreader { 98 | const char *s; 99 | size_t len; 100 | void *data; 101 | breader readf; 102 | int cursor; 103 | }; 104 | 105 | struct blexerbuf { 106 | char *s; 107 | size_t len, size; 108 | }; 109 | 110 | typedef struct btoken { 111 | btokentype type; 112 | union { 113 | bstring *s; 114 | bint i; 115 | breal r; 116 | } u; 117 | } btoken; 118 | 119 | typedef struct blexer { 120 | const char *fname; 121 | btoken token; 122 | int linenumber; 123 | int lastline; 124 | btokentype cacheType; 125 | struct blexerbuf buf; 126 | struct blexerreader reader; 127 | bmap *strtab; 128 | bvm *vm; 129 | int had_whitespace; /* track if whitespace/newline preceded current token */ 130 | } blexer; 131 | 132 | void be_lexer_init(blexer *lexer, bvm *vm, 133 | const char *fname, breader reader, void *data); 134 | void be_lexer_deinit(blexer *lexer); 135 | void be_lexerror(blexer *lexer, const char *msg); 136 | int be_lexer_scan_next(blexer *lexer); 137 | bstring* be_lexer_newstr(blexer *lexer, const char *str); 138 | const char *be_token2str(bvm *vm, btoken *token); 139 | const char* be_tokentype2str(btokentype type); 140 | char* be_load_unicode(char *dst, const char *src); 141 | 142 | #endif 143 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "main", "master" ] 17 | schedule: 18 | - cron: '0 0 * * *' 19 | # pull_request: 20 | # branches: '*' 21 | 22 | jobs: 23 | analyze: 24 | name: Analyze 25 | # Runner size impacts CodeQL analysis time. To learn more, please see: 26 | # - https://gh.io/recommended-hardware-resources-for-running-codeql 27 | # - https://gh.io/supported-runners-and-hardware-resources 28 | # - https://gh.io/using-larger-runners 29 | # Consider using larger runners for possible analysis time improvements. 30 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-20.04' }} 31 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 32 | permissions: 33 | actions: read 34 | contents: read 35 | security-events: write 36 | 37 | strategy: 38 | fail-fast: false 39 | matrix: 40 | language: [ 'cpp' ] 41 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby', 'swift' ] 42 | # Use only 'java' to analyze code written in Java, Kotlin or both 43 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 44 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 45 | 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v3 49 | with: 50 | submodules: recursive 51 | 52 | # Initializes the CodeQL tools for scanning. 53 | - name: Initialize CodeQL 54 | uses: github/codeql-action/init@v2 55 | with: 56 | languages: ${{ matrix.language }} 57 | # If you wish to specify custom queries, you can do so here or in a config file. 58 | # By default, queries listed here will override any specified in a config file. 59 | # Prefix the list here with "+" to use these queries and those in the config file. 60 | 61 | # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 62 | # queries: security-extended,security-and-quality 63 | queries: security-and-quality 64 | 65 | 66 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). 67 | # If this step fails, then you should remove it and run the build manually (see below) 68 | #- name: Autobuild 69 | # uses: github/codeql-action/autobuild@v2 70 | 71 | # ℹ️ Command-line programs to run using the OS shell. 72 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 73 | 74 | # If the Autobuild fails above, remove it and uncomment the following three lines. 75 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 76 | 77 | - run: | 78 | ./.github/workflows/codeql-buildscript.sh 79 | 80 | - name: Perform CodeQL Analysis 81 | uses: github/codeql-action/analyze@v2 82 | with: 83 | category: "/language:${{matrix.language}}" 84 | upload: false 85 | id: step1 86 | 87 | # Filter out rules with low severity or high false positve rate 88 | # Also filter out warnings in third-party code 89 | - name: Filter out unwanted errors and warnings 90 | uses: advanced-security/filter-sarif@v1 91 | with: 92 | patterns: | 93 | -**:cpp/path-injection 94 | -**:cpp/world-writable-file-creation 95 | -**:cpp/poorly-documented-function 96 | -**:cpp/potentially-dangerous-function 97 | -**:cpp/use-of-goto 98 | -**:cpp/integer-multiplication-cast-to-long 99 | -**:cpp/comparison-with-wider-type 100 | -**:cpp/leap-year/* 101 | -**:cpp/ambiguously-signed-bit-field 102 | -**:cpp/suspicious-pointer-scaling 103 | -**:cpp/suspicious-pointer-scaling-void 104 | -**:cpp/unsigned-comparison-zero 105 | -**/cmake*/Modules/** 106 | input: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif 107 | output: ${{ steps.step1.outputs.sarif-output }}/cpp.sarif 108 | 109 | - name: Upload CodeQL results to code scanning 110 | uses: github/codeql-action/upload-sarif@v2 111 | with: 112 | sarif_file: ${{ steps.step1.outputs.sarif-output }} 113 | category: "/language:${{matrix.language}}" 114 | 115 | - name: Upload CodeQL results as an artifact 116 | if: success() || failure() 117 | uses: actions/upload-artifact@v3 118 | with: 119 | name: codeql-results 120 | path: ${{ steps.step1.outputs.sarif-output }} 121 | retention-days: 5 122 | -------------------------------------------------------------------------------- /src/be_vector.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_vector.h" 9 | #include "be_mem.h" 10 | #include 11 | 12 | /* initialize a vector, the vector structure itself is usually allocated 13 | * on the stack, and the data is allocated from the heap. 14 | **/ 15 | void be_vector_init(bvm *vm, bvector *vector, int size) 16 | { 17 | vector->capacity = 2; /* the default capacity */ 18 | vector->size = size; 19 | vector->count = 0; 20 | vector->data = be_malloc(vm, (size_t)vector->capacity * size); 21 | vector->end = (char*)vector->data - size; 22 | memset(vector->data, 0, (size_t)vector->capacity * size); 23 | } 24 | 25 | void be_vector_delete(bvm *vm, bvector *vector) 26 | { 27 | be_free(vm, vector->data, (size_t)vector->capacity * vector->size); 28 | } 29 | 30 | void* be_vector_at(bvector *vector, int index) 31 | { 32 | return (char*)vector->data + (size_t)index * vector->size; 33 | } 34 | 35 | void be_vector_push(bvm *vm, bvector *vector, void *data) 36 | { 37 | size_t size = vector->size; 38 | size_t capacity = vector->capacity; 39 | size_t count = vector->count++; 40 | if (count >= capacity) { 41 | int newcap = be_nextsize(vector->capacity); 42 | vector->data = be_realloc(vm, 43 | vector->data, vector->capacity * size, newcap * size); 44 | vector->end = (char*)vector->data + count * size; 45 | vector->capacity = newcap; 46 | } else { 47 | vector->end = (char*)vector->end + size; 48 | } 49 | if (data != NULL) { 50 | memcpy(vector->end, data, size); 51 | } 52 | } 53 | 54 | /* clear the expanded portion if the memory expands */ 55 | void be_vector_push_c(bvm *vm, bvector *vector, void *data) 56 | { 57 | int capacity = vector->capacity + 1; 58 | be_vector_push(vm, vector, data); 59 | if (vector->capacity > capacity) { 60 | size_t size = ((size_t)vector->capacity - capacity) * vector->size; 61 | memset(be_vector_at(vector, capacity), 0, size); 62 | } 63 | } 64 | 65 | void be_vector_remove_end(bvector *vector) 66 | { 67 | be_assert(vector->count > 0); 68 | vector->count--; 69 | vector->end = (char*)vector->end - vector->size; 70 | } 71 | 72 | void be_vector_resize(bvm *vm, bvector *vector, int count) 73 | { 74 | size_t size = vector->size; 75 | be_assert(count >= 0); 76 | if (count != be_vector_count(vector)) { 77 | int newcap = be_nextsize(count); 78 | if (newcap > vector->capacity) { /* extended capacity */ 79 | vector->data = be_realloc(vm, 80 | vector->data, vector->capacity * size, newcap * size); 81 | vector->capacity = newcap; 82 | } 83 | vector->count = count; 84 | if (count == 0) { 85 | vector->end = (char*)vector->data - size; 86 | } else { 87 | vector->end = (char*)vector->data + size * ((size_t)count - 1); 88 | } 89 | } 90 | } 91 | 92 | void be_vector_clear(bvector *vector) 93 | { 94 | vector->count = 0; 95 | vector->end = (char*)vector->data - vector->size; 96 | } 97 | 98 | /* free not used */ 99 | void* be_vector_release(bvm *vm, bvector *vector) 100 | { 101 | size_t size = vector->size; 102 | int count = be_vector_count(vector); 103 | if (count == 0) { 104 | be_free(vm, vector->data, vector->capacity * size); 105 | vector->capacity = 0; 106 | vector->data = NULL; 107 | vector->end = NULL; 108 | } else if (count < vector->capacity) { 109 | vector->data = be_realloc(vm, 110 | vector->data, vector->capacity * size, count * size); 111 | vector->end = (char*)vector->data + ((size_t)count - 1) * size; 112 | vector->capacity = count; 113 | } 114 | return vector->data; 115 | } 116 | 117 | /* use binary search to find the vector capacity between 0-1024 */ 118 | static int binary_search(int value) 119 | { 120 | static const uint16_t tab[] = { 121 | 0, 2, 4, 6, 8, 10, 12, 14, 16, 122 | 20, 24, 28, 32, 40, 48, 64, 96, 128, 123 | 192, 256, 384, 512, 768, 1024 124 | }; 125 | const uint16_t *low = tab; 126 | const uint16_t *high = tab + array_count(tab) - 1; 127 | while (low <= high) { 128 | const uint16_t *mid = low + ((high - low) >> 1); 129 | if (*mid == value) { 130 | return mid[1]; 131 | } 132 | if (*mid < value) { 133 | low = mid + 1; 134 | } else { 135 | high = mid - 1; 136 | } 137 | } 138 | return *low; 139 | } 140 | 141 | static int nextpow(int value) 142 | { 143 | value |= value >> 1; 144 | value |= value >> 2; 145 | value |= value >> 4; 146 | value |= value >> 8; 147 | value |= value >> 16; 148 | return value + 1; 149 | } 150 | 151 | int be_nextsize(int size) 152 | { 153 | if (size < 1024) { 154 | return binary_search(size); 155 | } 156 | return nextpow(size); 157 | } 158 | -------------------------------------------------------------------------------- /src/be_func.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_func.h" 9 | #include "be_gc.h" 10 | #include "be_mem.h" 11 | #include "be_vm.h" 12 | #include "be_exec.h" 13 | #include 14 | 15 | #define clousersize(n) \ 16 | (sizeof(bclosure) + sizeof(bupval*) * ((size_t)(n) - 1)) 17 | 18 | static bupval* findupval(bvm *vm, bvalue *level) 19 | { 20 | bupval *node = vm->upvalist; 21 | while (node != NULL && node->value > level) { 22 | node = node->u.next; 23 | } 24 | if (!node || node->value != level) { 25 | /* not found */ 26 | node = be_malloc(vm, sizeof(bupval)); 27 | node->value = level; 28 | node->refcnt = 0; 29 | /* insert to list head */ 30 | node->u.next = vm->upvalist; 31 | vm->upvalist = node; 32 | } 33 | return node; 34 | } 35 | 36 | void be_initupvals(bvm *vm, bclosure *cl) 37 | { 38 | int count = cl->proto->nupvals; 39 | bupvaldesc *desc = cl->proto->upvals; 40 | bvalue *stack = vm->reg; 41 | bupval **uv = cl->upvals; 42 | bupval **superuv = cast(bclosure*, var_toobj(vm->cf->func))->upvals; 43 | for (; count--; desc++, uv++) { 44 | if (desc->instack) { 45 | bvalue *ref = stack + desc->idx; 46 | *uv = findupval(vm, ref); 47 | } else { 48 | *uv = superuv[desc->idx]; 49 | } 50 | (*uv)->refcnt++; 51 | } 52 | } 53 | 54 | void be_upvals_close(bvm *vm, bvalue *level) 55 | { 56 | bupval *node = vm->upvalist, *next; 57 | bupval **prev = &vm->upvalist; 58 | while (node) { 59 | next = node->u.next; 60 | if (node->value >= level) { 61 | if (!node->refcnt) { 62 | be_free(vm, node, sizeof(bupval)); 63 | } else { 64 | node->u.value = *node->value; /* move value to upvalue slot */ 65 | node->value = &node->u.value; 66 | } 67 | *prev = next; /* remove from linked list */ 68 | } else { 69 | prev = &node->u.next; 70 | } 71 | node = next; 72 | } 73 | } 74 | 75 | void be_release_upvalues(bvm *vm, bclosure *cl) 76 | { 77 | int i, count = cl->nupvals; 78 | for (i = 0; i < count; ++i) { 79 | bupval *uv = cl->upvals[i]; 80 | if (uv) { 81 | if (uv->refcnt) { 82 | --uv->refcnt; 83 | } 84 | /* delete non-referenced closed upvalue */ 85 | if (uv->value == &uv->u.value && !uv->refcnt) { 86 | be_free(vm, uv, sizeof(bupval)); 87 | } 88 | } 89 | } 90 | } 91 | 92 | bproto* be_newproto(bvm *vm) 93 | { 94 | bgcobject *gco = be_gcnew(vm, BE_PROTO, bproto); 95 | bproto *p = cast_proto(gco); 96 | if (p) { 97 | p->upvals = NULL; 98 | p->ktab = NULL; 99 | p->ptab = NULL; 100 | p->code = NULL; 101 | p->name = NULL; 102 | p->gray = NULL; 103 | p->codesize = 0; 104 | p->nupvals = 0; 105 | p->nproto = 0; 106 | p->nconst = 0; 107 | p->nstack = 0; 108 | p->codesize = 0; 109 | p->argc = 0; 110 | p->varg = 0; 111 | #if BE_DEBUG_SOURCE_FILE 112 | p->source = NULL; 113 | #endif 114 | #if BE_DEBUG_RUNTIME_INFO 115 | p->lineinfo = NULL; 116 | p->nlineinfo = 0; 117 | #endif 118 | #if BE_DEBUG_VAR_INFO 119 | p->varinfo = NULL; 120 | p->nvarinfo = 0; 121 | #endif 122 | } 123 | return p; 124 | } 125 | 126 | bclosure* be_newclosure(bvm *vm, int nupval) 127 | { 128 | bgcobject *gco = be_newgcobj(vm, BE_CLOSURE, clousersize(nupval)); 129 | bclosure *cl = cast_closure(gco); 130 | if (cl) { 131 | cl->proto = NULL; 132 | cl->nupvals = (bbyte)nupval; 133 | while (nupval--) { 134 | cl->upvals[nupval] = NULL; 135 | } 136 | } 137 | return cl; 138 | } 139 | 140 | static void init_upvals(bvm *vm, bntvclos *f) 141 | { 142 | int count = f->nupvals; 143 | bupval **upvals = &be_ntvclos_upval(f, 0); 144 | while (count--) { 145 | bupval *uv = be_malloc(vm, sizeof(bupval)); /* was closed */ 146 | uv->value = &uv->u.value; 147 | uv->refcnt = 1; 148 | var_setnil(uv->value); 149 | *upvals++ = uv; 150 | } 151 | } 152 | 153 | bntvclos* be_newntvclosure(bvm *vm, bntvfunc cf, int nupvals) 154 | { 155 | size_t size = sizeof(bntvclos) + sizeof(bupval*) * nupvals; 156 | bgcobject *gco = be_newgcobj(vm, BE_NTVCLOS, size); 157 | bntvclos *f = cast_ntvclos(gco); 158 | if (f) { 159 | f->f = cf; 160 | f->nupvals = (bbyte)nupvals; 161 | if (nupvals) { 162 | var_setntvclos(vm->top, f); 163 | be_incrtop(vm); 164 | init_upvals(vm, f); /* may be GC */ 165 | be_stackpop(vm, 1); 166 | } 167 | } 168 | return f; 169 | } 170 | 171 | #if BE_DEBUG_VAR_INFO 172 | bstring* be_func_varname(bproto *proto, int index, int pc) 173 | { 174 | int i, nvarinfo = proto->nvarinfo; 175 | bvarinfo *varinfo = proto->varinfo; 176 | for (i = 0; i < nvarinfo && varinfo[i].beginpc <= pc; ++i) { 177 | if (pc <= varinfo[i].endpc && index-- == 0) { 178 | return varinfo[i].name; 179 | } 180 | } 181 | return NULL; 182 | } 183 | #endif 184 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

3 | Berry 4 |

5 |

The Berry Script Language.

6 |

7 | 8 | ## Introduction 9 | 10 | Berry is a ultra-lightweight dynamically typed embedded scripting language. It is designed for lower-performance embedded devices. The Berry interpreter-core's code size is less than 40KiB and can run on less than 4KiB heap (on ARM Cortex M4 CPU, Thumb ISA and ARMCC compiler). 11 | 12 | The interpreter of Berry include a one-pass compiler and register-based VM, all the code is written in ANSI C99. In Berry not every type is a class object. Some simple value types, such as int, real, boolean and string are not class object, but list, map and range are class object. This is a consideration about performance. 13 | Register-based VM is the same meaning as above. 14 | 15 | Berry has the following advantages: 16 | 17 | * Lightweight: A well-optimized interpreter with very little resources. Ideal for use in microprocessors. 18 | * Fast: optimized one-pass bytecode compiler and register-based virtual machine. 19 | * Powerful: supports imperative programming, object-oriented programming, functional programming. 20 | * Flexible: Berry is a dynamic type script, and it's intended for embedding in applications. It can provide good dynamic scalability for the host system. 21 | * Simple: simple and natural syntax, support garbage collection, and easy to use FFI (foreign function interface). 22 | * RAM saving: With compile-time object construction, most of the constant objects are stored in read-only code data segments, so the RAM usage of the interpreter is very low when it starts. 23 | 24 | ## Documents 25 | 26 | Reference Manual: [Read the docs](https://berry.readthedocs.io/) 27 | 28 | Short Manual: [berry_short_manual.pdf](https://github.com/berry-lang/berry_doc/blob/master/pdf/berry_short_manual.pdf). 29 | 30 | Berry's EBNF grammar definition: [tools/grammar/berry.ebnf](./tools/grammar/berry.ebnf) 31 | 32 | ## Features 33 | 34 | * Base Type 35 | * Nil: `nil` 36 | * Boolean: `true` and `false` 37 | * Numerical: Integer (`int`) and Real (`real`) 38 | * String: Single quotation-mark string and double quotation-mark string 39 | * Class: Instance template, read only 40 | * Instance: Object constructed by class 41 | * Module: Read-write key-value pair table 42 | * List: Ordered container, like `[1, 2, 3]` 43 | * Map: Hash Map container, like `{ 'a': 1, 2: 3, 'map': {} }` 44 | * Range: include a lower and a upper integer value, like `0..5` 45 | * Operator and Expression 46 | * Assign operator: `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=` 47 | * Relational operator: `<`, `<=`, `==`, `!=`, `>`, `>=` 48 | * Logic operator: `&&`, `||`, `!` 49 | * Arithmetic operator: `+`, `-`, `*`, `/`, `%` 50 | * Bitwise operator: `&`, `|`, `~`, `^`, `<<`, `>>` 51 | * Field operator: `.` 52 | * Subscript operator: `[]` 53 | * Connect string operator: `+` 54 | * Conditional operator: `? :` 55 | * Brackets: `()` 56 | * Control Structure 57 | * Conditional statement: `if-else` 58 | * Iteration statement: `while` and `for` 59 | * Jump statement: `break` and `continue` 60 | * Function 61 | * Local variable and block scope 62 | * Return statement 63 | * Nested functions definition 64 | * Closure based on Upvalue 65 | * Anonymous function 66 | * Lambda expression 67 | * Class 68 | * Inheritance (only public single inheritance) 69 | * Method and Operator Overload 70 | * Constructor method 71 | * Destructive method 72 | * Module Management 73 | * Built-in module that takes almost no RAM 74 | * Extension module support: script module, bytecode file module and shared library (like *.so, *.dll) module 75 | * GC (Garbage collection) 76 | * Mark-Sweep GC 77 | * Exceptional Handling 78 | * Throw any exception value using the `raise` statement 79 | * Multiple catch mode 80 | * Bytecode file support 81 | * Export function to bytecode file 82 | * Load the bytecode file and execute 83 | 84 | ## Build and Run 85 | 86 | 1. Install the readline library (Windows does not need): 87 | 88 | ``` bash 89 | sudo apt install libreadline-dev # Ubuntu 90 | brew install readline # MacOS 91 | ``` 92 | 93 | 2. Build (The default compiler is GCC): 94 | 95 | ``` 96 | make 97 | ``` 98 | 99 | 3. Run: 100 | 101 | ``` bash 102 | ./berry # Bash or PowerShell 103 | berry # Windows CMD 104 | ``` 105 | 106 | 4. Install (Only Unix-like): 107 | 108 | ``` bash 109 | make install 110 | ``` 111 | 112 | ## Editor plugins 113 | 114 | [Visual Studio Code](https://code.visualstudio.com/) plugin are in this directory: [./tools/plugins/vscode](./tools/plugins/vscode). 115 | 116 | ## Examples 117 | 118 | After compiling successfully, use the `berry` command with no parameters to enter the REPL environment: 119 | ``` 120 | Berry 0.0.1 (build in Dec 24 2018, 18:12:49) 121 | [GCC 8.2.0] on Linux (default) 122 | > 123 | ``` 124 | 125 | Now enter this code: 126 | 127 | ``` lua 128 | print("Hello world!") 129 | ``` 130 | 131 | You will see this output: 132 | 133 | ``` 134 | Hello world! 135 | ``` 136 | 137 | You can copy this code to the REPL: 138 | 139 | ``` ruby 140 | def fib(x) 141 | if x <= 1 142 | return x 143 | end 144 | return fib(x - 1) + fib(x - 2) 145 | end 146 | fib(10) 147 | ``` 148 | 149 | This example code will output the result `55` and you can save the above code to a plain text file (eg test.be) and run this command: 150 | 151 | ``` bash 152 | ./berry test.be 153 | ``` 154 | 155 | This will also get the correct output. 156 | 157 | ## License 158 | 159 | Berry is free software distributed under the [MIT license](./LICENSE). 160 | 161 | The Berry interpreter partly referred to [Lua](http://www.lua.org/)'s design. View Lua's license here: http://www.lua.org/license.html. 162 | -------------------------------------------------------------------------------- /src/be_rangelib.c: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | ** Copyright (c) 2018-2020 Guan Wenliang 3 | ** This file is part of the Berry default interpreter. 4 | ** skiars@qq.com, https://github.com/Skiars/berry 5 | ** See Copyright Notice in the LICENSE file or at 6 | ** https://github.com/Skiars/berry/blob/master/LICENSE 7 | ********************************************************************/ 8 | #include "be_object.h" 9 | #include "be_func.h" 10 | #include "be_vm.h" 11 | 12 | static int m_init(bvm *vm) 13 | { 14 | int argc = be_top(vm); 15 | if (argc < 3) { be_raise(vm, "value_error", "missing arguments"); } 16 | if (!be_isint(vm, 2) || !be_isint(vm, 3)) { be_raise(vm, "value_error", "arguments must be 'int'"); } 17 | be_pushvalue(vm, 2); 18 | be_setmember(vm, 1, "__lower__"); 19 | be_pop(vm, 1); 20 | be_pushvalue(vm, 3); 21 | be_setmember(vm, 1, "__upper__"); 22 | int incr = 1; /* default increment is '1' */ 23 | if (argc >= 4) { 24 | if (!be_isint(vm, 4)) { be_raise(vm, "value_error", "arguments must be 'int'"); } 25 | incr = be_toint(vm, 4); 26 | if (incr == 0) { be_raise(vm, "value_error", "increment cannot be zero"); } 27 | } 28 | be_pushint(vm, incr); 29 | be_setmember(vm, 1, "__incr__"); 30 | be_return_nil(vm); 31 | } 32 | 33 | static int m_tostring(bvm *vm) 34 | { 35 | be_getmember(vm, 1, "__incr__"); 36 | int incr = be_toint(vm, -1); 37 | be_pop(vm, 1); 38 | if (incr == 1) { 39 | be_pushstring(vm, "("); 40 | be_getmember(vm, 1, "__lower__"); 41 | be_tostring(vm, -1); 42 | be_strconcat(vm, -2); 43 | be_pop(vm, 1); 44 | be_pushstring(vm, ".."); 45 | be_strconcat(vm, -2); 46 | be_pop(vm, 1); 47 | be_getmember(vm, 1, "__upper__"); 48 | be_tostring(vm, -1); 49 | be_strconcat(vm, -2); 50 | be_pop(vm, 1); 51 | be_pushstring(vm, ")"); 52 | be_strconcat(vm, -2); 53 | be_pop(vm, 1); 54 | } else { 55 | be_pushstring(vm, "range("); 56 | be_getmember(vm, 1, "__lower__"); 57 | be_tostring(vm, -1); 58 | be_strconcat(vm, -2); 59 | be_pop(vm, 1); 60 | be_pushstring(vm, ", "); 61 | be_strconcat(vm, -2); 62 | be_pop(vm, 1); 63 | be_getmember(vm, 1, "__upper__"); 64 | be_tostring(vm, -1); 65 | be_strconcat(vm, -2); 66 | be_pop(vm, 1); 67 | be_pushstring(vm, ", "); 68 | be_strconcat(vm, -2); 69 | be_pop(vm, 1); 70 | be_getmember(vm, 1, "__incr__"); 71 | be_tostring(vm, -1); 72 | be_strconcat(vm, -2); 73 | be_pop(vm, 1); 74 | be_pushstring(vm, ")"); 75 | be_strconcat(vm, -2); 76 | be_pop(vm, 1); 77 | } 78 | be_return(vm); 79 | } 80 | 81 | static int m_upper(bvm *vm) 82 | { 83 | be_getmember(vm, 1, "__upper__"); 84 | be_return(vm); 85 | } 86 | 87 | static int m_lower(bvm *vm) 88 | { 89 | be_getmember(vm, 1, "__lower__"); 90 | be_return(vm); 91 | } 92 | 93 | static int m_incr(bvm *vm) 94 | { 95 | be_getmember(vm, 1, "__incr__"); 96 | be_return(vm); 97 | } 98 | 99 | static int m_setrange(bvm *vm) 100 | { 101 | int argc = be_top(vm); 102 | if (argc < 3) { be_raise(vm, "value_error", "missing arguments"); } 103 | if (!be_isint(vm, 2) || !be_isint(vm, 3)) { be_raise(vm, "value_error", "arguments must be 'int'"); } 104 | be_pushvalue(vm, 2); 105 | be_setmember(vm, 1, "__lower__"); 106 | be_pop(vm, 1); 107 | be_pushvalue(vm, 3); 108 | be_setmember(vm, 1, "__upper__"); 109 | int incr = 1; /* default increment is '1' */ 110 | if (argc >= 4) { 111 | if (!be_isint(vm, 4)) { be_raise(vm, "value_error", "arguments must be 'int'"); } 112 | incr = be_toint(vm, 4); 113 | if (incr == 0) { be_raise(vm, "value_error", "increment cannot be zero"); } 114 | } 115 | be_pushint(vm, incr); 116 | be_setmember(vm, 1, "__incr__"); 117 | be_return_nil(vm); 118 | } 119 | 120 | static int iter_closure(bvm *vm) 121 | { 122 | /* for better performance, we operate the upvalues 123 | * directly without using by the stack. */ 124 | bntvclos *func = var_toobj(vm->cf->func); 125 | bvalue *uv0 = be_ntvclos_upval(func, 0)->value; 126 | bvalue *uv1 = be_ntvclos_upval(func, 1)->value; 127 | bvalue *uv2 = be_ntvclos_upval(func, 2)->value; 128 | bint lower = var_toint(uv0); /* upvalue[0] => lower */ 129 | bint upper = var_toint(uv1); /* upvalue[1] => upper */ 130 | bint incr = var_toint(uv2); /* upvalue[2] => incr */ 131 | if ((incr > 0 && lower > upper) || (incr < 0 && lower < upper)) { 132 | be_stop_iteration(vm); 133 | } 134 | var_toint(uv0) = lower + incr; /* set upvale[0] */ 135 | be_pushint(vm, lower); /* push the return value */ 136 | be_return(vm); 137 | } 138 | 139 | static int m_iter(bvm *vm) 140 | { 141 | be_pushntvclosure(vm, iter_closure, 3); 142 | be_getmember(vm, 1, "__lower__"); 143 | be_setupval(vm, -2, 0); 144 | be_pop(vm, 1); 145 | be_getmember(vm, 1, "__upper__"); 146 | be_setupval(vm, -2, 1); 147 | be_pop(vm, 1); 148 | be_getmember(vm, 1, "__incr__"); 149 | be_setupval(vm, -2, 2); 150 | be_pop(vm, 1); 151 | be_return(vm); 152 | } 153 | 154 | #if !BE_USE_PRECOMPILED_OBJECT 155 | void be_load_rangelib(bvm *vm) 156 | { 157 | static const bnfuncinfo members[] = { 158 | { "__lower__", NULL }, 159 | { "__upper__", NULL }, 160 | { "__incr__", NULL }, 161 | { "init", m_init }, 162 | { "tostring", m_tostring }, 163 | { "lower", m_lower }, 164 | { "upper", m_upper }, 165 | { "setrange", m_setrange }, 166 | { "iter", m_iter }, 167 | { NULL, NULL } 168 | }; 169 | be_regclass(vm, "range", members); 170 | } 171 | #else 172 | /* @const_object_info_begin 173 | class be_class_range (scope: global, name: range) { 174 | __lower__, var 175 | __upper__, var 176 | __incr__, var 177 | init, func(m_init) 178 | tostring, func(m_tostring) 179 | lower, func(m_lower) 180 | upper, func(m_upper) 181 | incr, func(m_incr) 182 | setrange, func(m_setrange) 183 | iter, func(m_iter) 184 | } 185 | @const_object_info_end */ 186 | #include "../generate/be_fixed_be_class_range.h" 187 | #endif 188 | -------------------------------------------------------------------------------- /tools/coc/block_builder.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import json 3 | from hash_map import * 4 | 5 | class block: 6 | def __init__(self): 7 | self.type = "" 8 | self.name = "" 9 | self.attr = {} 10 | self.data = {} 11 | self.data_ordered = [] 12 | 13 | def depend(obj, macro): 14 | if "depend" in obj.attr: 15 | return macro.query(obj.attr["depend"]) 16 | else: 17 | return True 18 | 19 | class block_builder: 20 | """Output an object""" 21 | 22 | def __init__(self, obj, macro): 23 | self.block = block() 24 | self.strtab = [] 25 | self.strtab_weak = [] 26 | self.strtab_long = [] 27 | 28 | self.block.name = obj.name 29 | if depend(obj, macro): 30 | self.block.type = obj.type 31 | self.block.attr = obj.attr 32 | 33 | if "name" in obj.attr: 34 | if not self.get_strings_literal(self.block): 35 | self.strtab.append(obj.attr["name"]) 36 | else: 37 | self.strtab_weak.append(obj.attr["name"]) 38 | 39 | for key in obj.data_ordered: 40 | second = obj.data[key] 41 | if second.depend == None or macro.query(second.depend): 42 | self.block.data[key] = second.value 43 | if not self.get_strings_literal(self.block): 44 | self.strtab.append(key) 45 | else: 46 | self.strtab_weak.append(key) 47 | self.block.data_ordered.append(key) 48 | 49 | def block_tostring(self, block): 50 | ostr = "" 51 | if block.type == "map": 52 | ostr += self.map_tostring(block, block.name, False, self.get_strings_literal(block)) 53 | elif block.type == "class": 54 | ostr += self.class_tostring(block) 55 | elif block.type == "vartab": 56 | ostr += self.vartab_tostring(block) 57 | elif block.type == "module": 58 | ostr += self.module_tostring(block) 59 | return ostr 60 | 61 | def class_tostring(self, block): 62 | ostr = "" 63 | hmap = hash_map(block.data) 64 | map_name = block.name + "_map" 65 | if len(block.data) > 0: 66 | ostr += self.map_tostring(block, map_name, True, self.get_strings_literal(block)) + "\n" 67 | 68 | ostr += self.scope(block) + " be_define_const_class(\n " 69 | ostr += block.name + ",\n " 70 | ostr += str(hmap.var_count()) + ",\n " 71 | ostr += self.get_super(block) + ",\n " 72 | ostr += self.name(block) + "\n);\n" 73 | return ostr 74 | 75 | def map_tostring(self, block, name, local, literal): 76 | hmap = hash_map(block.data) 77 | entlist = hmap.entry_list() 78 | ostr = "" 79 | 80 | ostr += "static be_define_const_map_slots(" + name + ") {\n" 81 | for ent in entlist: 82 | if literal: 83 | ostr += " { be_const_key_weak(" + ent.key + ", " 84 | else: 85 | ostr += " { be_const_key(" + ent.key + ", " 86 | ostr += str(ent.next) + "), " + ent.value + " },\n" 87 | ostr += "};\n\n" 88 | 89 | if local: 90 | ostr += "static" 91 | else: 92 | ostr += self.scope(block) 93 | ostr += " be_define_const_map(\n " 94 | ostr += name + ",\n " 95 | ostr += str(len(entlist)) + "\n);\n" 96 | return ostr 97 | 98 | def vartab_tostring(self, block): 99 | ostr = "" 100 | varvec = [] 101 | idxblk = copy.deepcopy(block) 102 | index = 0 103 | idxblk.data = {} 104 | for key in block.data_ordered: 105 | varvec.append(block.data[key]) 106 | idxblk.data[key] = "int(" + str(index) + ")" 107 | index += 1 108 | 109 | ostr += self.map_tostring(idxblk, block.name + "_map", True, False) + "\n" 110 | ostr += "static const bvalue __vlist_array[] = {\n"; 111 | for it in varvec: 112 | ostr += " be_const_" + it + ",\n" 113 | ostr += "};\n\n" 114 | 115 | ostr += "static be_define_const_vector(\n " 116 | ostr += block.name + "_vector,\n __vlist_array,\n " 117 | ostr += str(len(varvec)) + "\n);\n" 118 | return ostr 119 | 120 | def module_tostring(self, block): 121 | ostr = "" 122 | name = "m_lib" + block.name 123 | map_name = name + "_map" 124 | 125 | ostr += self.map_tostring(block, map_name, True, self.get_strings_literal(block)) + "\n" 126 | ostr += "static be_define_const_module(\n " 127 | ostr += name + ",\n " 128 | ostr += "\"" + block.name + "\"\n);\n" 129 | scp = self.scope(block) 130 | if scp != "static": 131 | ostr += "\n" + scp 132 | ostr += " be_define_const_native_module(" 133 | ostr += block.name + ");\n" 134 | return ostr 135 | 136 | def scope(self, block): 137 | if "local" in block.attr: 138 | return "static" 139 | else: 140 | return "BE_EXPORT_VARIABLE" 141 | 142 | def get_super(self, block): 143 | if "super" in block.attr: 144 | return "(bclass *)&" + block.attr["super"] 145 | else: 146 | return "NULL" 147 | 148 | def get_strings_literal(self, block): 149 | if "strings" in block.attr: 150 | a = block.attr["strings"] 151 | return block.attr["strings"] == "weak" 152 | else: 153 | return False 154 | 155 | def name(self, block): 156 | if "name" in block.attr: 157 | return block.attr["name"] 158 | else: 159 | return block.name 160 | 161 | def writefile(self, filename, text): 162 | otext = "#include \"be_constobj.h\"\n\n" + text 163 | with open(filename, "w", encoding='utf-8') as f: 164 | f.write(otext) 165 | 166 | def dumpfile(self, path): 167 | s = self.block_tostring(self.block) 168 | if "file" in self.block.attr: 169 | name = self.block.attr["file"] 170 | else: 171 | name = self.block.name 172 | self.writefile(path + "/be_fixed_" + name + ".h", s) 173 | --------------------------------------------------------------------------------