├── test ├── fixtures │ ├── p.rb │ ├── assign.rb │ ├── .gitignore │ ├── operator_1.rb │ ├── puts_int.rb │ ├── puts_symbol.rb │ ├── syntax_error.rb │ ├── assign_puts.rb │ ├── hello_world.rb │ ├── class_1.rb │ ├── space_semicolon.rb │ ├── interpolation.rb │ ├── array_1.rb │ ├── while.rb │ ├── class_2.rb │ ├── crlf.rb │ ├── hash_1.rb │ ├── method_1.rb │ ├── case.rb │ ├── qwords.rb │ ├── larger_script.rb │ ├── co2.rb │ ├── thermistor.rb │ ├── secondary.rb │ ├── led.rb │ ├── primary.rb │ ├── wifi.rb │ └── keymap.rb ├── misc │ ├── .gitignore │ ├── def.rb │ └── array_each.rb ├── lvar_test.rb ├── rightward_assign_test.rb ├── keyword_test.rb ├── float_test.rb ├── module_test.rb ├── and_or_test.rb ├── endless_def_test.rb ├── gen_values_test.rb ├── lambda_test.rb ├── dstr_test.rb ├── unary_test.rb ├── scall_test.rb ├── op_assign_test.rb ├── rest_arg_test.rb ├── string_test.rb ├── method_test.rb ├── function_test.rb ├── assign_test.rb ├── ivar_test.rb ├── gvar_test.rb ├── class_test.rb ├── singleton_test.rb ├── super_test.rb ├── splat_test.rb ├── namespace_test.rb ├── array_test.rb ├── yield_test.rb ├── integer_test.rb ├── case_test.rb ├── karg_test.rb ├── hash_test.rb ├── if_test.rb ├── block_test.rb ├── rescue_test.rb ├── masgn_test.rb ├── def_test.rb └── helper │ └── test.rb ├── .gitignore ├── include ├── .gitignore ├── version.h ├── ptr_size.h ├── mrbgem.h ├── compiler.h ├── my_regex.h ├── node.h ├── stream.h ├── dump.h ├── picorbc.h ├── tokenizer.h ├── token_data.h ├── context.h ├── regex.h ├── common.h ├── value.h ├── generator.h ├── banned.h ├── mrb_state │ └── microrb.h ├── token.h ├── debug.h ├── scope.h ├── parse_header.h └── opcode.h ├── src ├── .gitignore ├── mrbgem.c ├── token.c ├── context.c ├── node.c ├── stream.c ├── common.c ├── my_regex.c ├── microrb.c ├── compiler.c ├── regex.c ├── scope.c └── dump.c ├── lib ├── .gitignore ├── ptr_size_generator.c └── Semantic-value-of-mid-rule-action-in-Lemon.md ├── mruby-pico-compiler.gem ├── README.md ├── Rakefile ├── LICENSE └── mrbgem.rake /test/fixtures/p.rb: -------------------------------------------------------------------------------- 1 | p 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | mruby/ 2 | tags 3 | -------------------------------------------------------------------------------- /test/fixtures/assign.rb: -------------------------------------------------------------------------------- 1 | a=:a 2 | -------------------------------------------------------------------------------- /test/misc/.gitignore: -------------------------------------------------------------------------------- 1 | *.mrb 2 | -------------------------------------------------------------------------------- /test/fixtures/.gitignore: -------------------------------------------------------------------------------- 1 | *.mrb 2 | *.c 3 | -------------------------------------------------------------------------------- /test/fixtures/operator_1.rb: -------------------------------------------------------------------------------- 1 | 1 + 2 * 3 2 | -------------------------------------------------------------------------------- /test/fixtures/puts_int.rb: -------------------------------------------------------------------------------- 1 | puts 65536 2 | -------------------------------------------------------------------------------- /test/fixtures/puts_symbol.rb: -------------------------------------------------------------------------------- 1 | puts :sym 2 | -------------------------------------------------------------------------------- /test/fixtures/syntax_error.rb: -------------------------------------------------------------------------------- 1 | [(]:a 2 | -------------------------------------------------------------------------------- /include/.gitignore: -------------------------------------------------------------------------------- 1 | *_helper.h 2 | parse.h 3 | -------------------------------------------------------------------------------- /test/fixtures/assign_puts.rb: -------------------------------------------------------------------------------- 1 | a=1;puts(a) 2 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | parse.out 2 | parse.c 3 | parse.h 4 | -------------------------------------------------------------------------------- /test/fixtures/hello_world.rb: -------------------------------------------------------------------------------- 1 | puts "Hello World!" 2 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | lemon 2 | ptr_size_generator 3 | *.d 4 | -------------------------------------------------------------------------------- /test/fixtures/class_1.rb: -------------------------------------------------------------------------------- 1 | class MyClass < Object 2 | end 3 | -------------------------------------------------------------------------------- /test/fixtures/space_semicolon.rb: -------------------------------------------------------------------------------- 1 | ; 2 | ; 3 | 4 | 5 | ; 6 | -------------------------------------------------------------------------------- /test/fixtures/interpolation.rb: -------------------------------------------------------------------------------- 1 | bbb = 'BBB' 2 | "aa#{bbb}ccc" 3 | -------------------------------------------------------------------------------- /test/fixtures/array_1.rb: -------------------------------------------------------------------------------- 1 | array[1] = '1' 2 | array[some_method(0)] 3 | -------------------------------------------------------------------------------- /test/fixtures/while.rb: -------------------------------------------------------------------------------- 1 | while (true) do 2 | 1 3 | end 4 | while 1 5 | end 6 | -------------------------------------------------------------------------------- /test/misc/def.rb: -------------------------------------------------------------------------------- 1 | def my_method(arg) 2 | arg 3 | end 4 | puts my_method("Hey") 5 | -------------------------------------------------------------------------------- /test/fixtures/class_2.rb: -------------------------------------------------------------------------------- 1 | class MyClass < Object 2 | def my_method(arg1, arg2) 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/fixtures/crlf.rb: -------------------------------------------------------------------------------- 1 | # This test is written in CRLF 2 | ary = %i[ 3 | a b 4 | c] 5 | p ary 6 | -------------------------------------------------------------------------------- /test/fixtures/hash_1.rb: -------------------------------------------------------------------------------- 1 | { key: "value" } 2 | { 'key' => value } 3 | { 4 | a: 0, 5 | "b" => 2, 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/method_1.rb: -------------------------------------------------------------------------------- 1 | object.call(arg1, arg2) 2 | object. 3 | call( arg ) 4 | object.method - 1 5 | -------------------------------------------------------------------------------- /test/fixtures/case.rb: -------------------------------------------------------------------------------- 1 | case a 2 | when 1 3 | do_shomethig 4 | end 5 | case a 6 | when 1; do_shomethig 7 | end 8 | -------------------------------------------------------------------------------- /include/version.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_VERSION_H_ 2 | #define PICORBC_VERSION_H_ 3 | 4 | #define PICORBC_VERSION "3.1.0dev" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/ptr_size.h: -------------------------------------------------------------------------------- 1 | #ifdef __SIZEOF_POINTER__ 2 | #define PICORBC_PTR_SIZE __SIZEOF_POINTER__ 3 | #else 4 | #define PICORBC_PTR_SIZE 8 5 | #endif 6 | 7 | -------------------------------------------------------------------------------- /test/lvar_test.rb: -------------------------------------------------------------------------------- 1 | class LvarTest < PicoRubyTest 2 | desc "Implicit initialize" 3 | assert_equal(<<~RUBY, 'nil') 4 | p(a=a) 5 | RUBY 6 | end 7 | -------------------------------------------------------------------------------- /test/rightward_assign_test.rb: -------------------------------------------------------------------------------- 1 | class RightwardAssignTest < PicoRubyTest 2 | assert_equal(<<~RUBY, "hello") 3 | :hello.to_s => a 4 | puts a 5 | RUBY 6 | end 7 | -------------------------------------------------------------------------------- /include/mrbgem.h: -------------------------------------------------------------------------------- 1 | #ifndef DISABLE_MRUBY 2 | #include 3 | 4 | void mrb_mruby_pico_compiler_gem_init(mrb_state *mrb); 5 | void mrb_mruby_pico_compiler_gem_final(mrb_state *mrb); 6 | #endif 7 | -------------------------------------------------------------------------------- /src/mrbgem.c: -------------------------------------------------------------------------------- 1 | #ifndef DISABLE_MRUBY 2 | #include 3 | 4 | void mrb_mruby_pico_compiler_gem_init(mrb_state *mrb){} 5 | void mrb_mruby_pico_compiler_gem_final(mrb_state *mrb){} 6 | #endif 7 | -------------------------------------------------------------------------------- /test/fixtures/qwords.rb: -------------------------------------------------------------------------------- 1 | %i(1 2 4) 2 | %i(1 3l 4 ) 3 | %i( 4 | aa 5 | bbbb cccc 6 | ) 7 | %i[ 8 | a 9 | b 10 | ] 11 | %w(1 2 4) 12 | %w(1 3l 4 ) 13 | %w( 14 | aa 15 | bbbb cccc 16 | ) 17 | %w[ 18 | a 19 | b 20 | ] 21 | -------------------------------------------------------------------------------- /test/keyword_test.rb: -------------------------------------------------------------------------------- 1 | class KeywordTest < PicoRubyTest 2 | desc "a keyword can be a method name" 3 | assert_equal(<<~RUBY, 'OK') 4 | def next(v) 5 | puts v 6 | end 7 | self.next("OK") 8 | RUBY 9 | end 10 | -------------------------------------------------------------------------------- /test/float_test.rb: -------------------------------------------------------------------------------- 1 | class FloatTest < PicoRubyTest 2 | desc "plus" 3 | assert_equal(<<~RUBY, "1.1") 4 | p 1.1 5 | RUBY 6 | 7 | desc "minus" 8 | assert_equal(<<~RUBY, "-1.1") 9 | p -1.1 10 | RUBY 11 | 12 | end 13 | -------------------------------------------------------------------------------- /test/misc/array_each.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | def each 3 | i = 0 4 | while i < length 5 | yield self[i] 6 | i += 1 7 | end 8 | return self 9 | end 10 | end 11 | 12 | [0,3].each do |a| 13 | p a 14 | end 15 | -------------------------------------------------------------------------------- /test/fixtures/larger_script.rb: -------------------------------------------------------------------------------- 1 | ary = Array.new(3) 2 | ary[0] = {a: 123e2} 3 | ary[0][:key] = "string" 4 | ary[1] = %w(abc ABC Hello) 5 | ary[2] = 0x1f 6 | ary[3] = !true 7 | puts "my name is #{self.class}, result is #{ary[2] * ary[0][:a]}" 8 | p ary 9 | -------------------------------------------------------------------------------- /test/fixtures/co2.rb: -------------------------------------------------------------------------------- 1 | class Co2 2 | def initialize 3 | @value = 0 4 | end 5 | def concentrate 6 | res = get_co2 7 | if res[0] == 255 && res[1] == 134 8 | res[2] * 256 + res[3] 9 | else 10 | 0 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /mruby-pico-compiler.gem: -------------------------------------------------------------------------------- 1 | name: mruby-pico-compiler 2 | description: PicoCompiler class 3 | author: HASUMI Hitoshi 4 | website: https://github.com/hasumikin/mruby-pico-compiler 5 | protocol: git 6 | repository: https://github.com/hasumikin/mruby-pico-compiler.git 7 | license: MIT 8 | -------------------------------------------------------------------------------- /test/module_test.rb: -------------------------------------------------------------------------------- 1 | class ModuleTest < PicoRubyTest 2 | 3 | if @@vm_select == :mruby 4 | 5 | desc "include a Module" 6 | assert_equal(<<~RUBY, "0") 7 | module A 8 | def a 9 | p 0 10 | end 11 | end 12 | include A 13 | self.a 14 | RUBY 15 | end 16 | 17 | end 18 | 19 | -------------------------------------------------------------------------------- /test/and_or_test.rb: -------------------------------------------------------------------------------- 1 | class AndOrTest < PicoRubyTest 2 | desc "and" 3 | assert_equal(<<~RUBY, "nil") 4 | p true && nil 5 | RUBY 6 | 7 | desc "or 1" 8 | assert_equal(<<~RUBY, "true") 9 | puts nil || true 10 | RUBY 11 | 12 | desc "or 2" 13 | assert_equal(<<~RUBY, "true") 14 | puts true || false 15 | RUBY 16 | end 17 | -------------------------------------------------------------------------------- /test/endless_def_test.rb: -------------------------------------------------------------------------------- 1 | class EndlessDefTest < PicoRubyTest 2 | desc "endless def arg" 3 | assert_equal(<<~RUBY, "55") 4 | def fib(n) = n < 2 ? n : fib(n-1) + fib(n-2) 5 | p fib(10) 6 | RUBY 7 | 8 | desc "endless def command" 9 | assert_equal(<<~RUBY, "you!") 10 | def hey(word) = puts word 11 | hey "you!" 12 | RUBY 13 | end 14 | -------------------------------------------------------------------------------- /test/gen_values_test.rb: -------------------------------------------------------------------------------- 1 | class GenValuesTest < PicoRubyTest 2 | 3 | if @@vm_select == :mruby 4 | 5 | desc "gen_values() in generator.c" 6 | assert_equal(<<~RUBY, "1\n[2, 3, 4]\n{:a=>0}\n{}\nhello") 7 | a = ->(m,*rest,m2,**opts,&block) do 8 | p m,rest,m2,opts; block.call 9 | end 10 | a.call(1,2,3,4,{a: 0}) {puts :hello} 11 | RUBY 12 | 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /test/fixtures/thermistor.rb: -------------------------------------------------------------------------------- 1 | B = 3435 2 | To = 25 3 | V = 3300 # mV 4 | Rref = 10_000 # Ohm 5 | 6 | class Thermistor 7 | def initialize 8 | gpio_init_output(0) 9 | gpio_set_level(0, 1) 10 | init_adc 11 | end 12 | 13 | def temperature 14 | vref = read_adc 15 | r = (V - vref).to_f / (vref.to_f/ Rref) 16 | 1.to_f / ( 1.to_f / B * Math.log(r / Rref) + 1.to_f / (To + 273) ) - 273 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/fixtures/secondary.rb: -------------------------------------------------------------------------------- 1 | sleep 80 # wait until CO2 sensor is warmed up 2 | 3 | debugprint('start', 'sub_loop') 4 | 5 | #wifi = Wifi.new 6 | 7 | while true 8 | co2 = $co2.concentrate 9 | temperature = $thermistor.temperature 10 | if co2 > 0 11 | data = "co2=#{co2}&temperature=#{temperature}" 12 | puts "DATASEND:#{data}" 13 | #wifi.post(data) 14 | sleep 300 15 | else 16 | sleep 3 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/lambda_test.rb: -------------------------------------------------------------------------------- 1 | class LambdaTest < PicoRubyTest 2 | 3 | if @@vm_select == :mruby 4 | 5 | desc "lambda call" 6 | assert_equal(<<~RUBY, "1\n2\n[3]\n4") 7 | a = -> (m,o=true,*rest,m2) { p m,o,rest,m2 } 8 | a.call(1,2,3,4) 9 | RUBY 10 | 11 | desc "a.() equivalent to a.call()" 12 | assert_equal(<<~RUBY, "1") 13 | a = -> (m) { p m } 14 | a.(1) 15 | RUBY 16 | 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /test/dstr_test.rb: -------------------------------------------------------------------------------- 1 | class DstrTest < PicoRubyTest 2 | desc "interpolation 1" 3 | assert_equal(<<~'RUBY', 'hello Ruby') 4 | @ivar = "Ruby" 5 | puts "hello #{@ivar}" 6 | RUBY 7 | 8 | desc "interpolation 2" 9 | assert_equal(<<~'RUBY', '2a') 10 | puts "#{1+1}" + "a" 11 | RUBY 12 | 13 | desc "interpolation 3" 14 | assert_equal(<<~'RUBY', '_101ab') 15 | i = 100 16 | puts "_" + "#{i+1}a" + "b" 17 | RUBY 18 | end 19 | -------------------------------------------------------------------------------- /test/unary_test.rb: -------------------------------------------------------------------------------- 1 | class UnaryTest < PicoRubyTest 2 | desc "p -1" 3 | assert_equal(<<~RUBY, "-1") 4 | p -1 5 | RUBY 6 | 7 | desc "after **(pow)" 8 | assert_equal(<<~RUBY, @@vm_select == :mrubyc ? "0" : "0.01") 9 | p 10**-2 10 | RUBY 11 | 12 | desc "-1**2" 13 | assert_equal(<<~RUBY, "-1") 14 | p -1**2 15 | RUBY 16 | 17 | desc "-1.3**3" 18 | assert_equal(<<~RUBY, "-2.197") 19 | p -1.3**3 20 | RUBY 21 | end 22 | -------------------------------------------------------------------------------- /test/fixtures/led.rb: -------------------------------------------------------------------------------- 1 | class Led 2 | def initialize(pin) 3 | @pin = pin 4 | gpio_init_output(@pin) 5 | turn_off 6 | @value = 1 7 | end 8 | 9 | def turn_on 10 | return nil if @value == 1 11 | gpio_set_level(@pin, 1) 12 | @value = 1 13 | # puts "turned on" 14 | end 15 | 16 | def turn_off 17 | return nil if @value == 0 18 | gpio_set_level(@pin, 0) 19 | @value = 0 20 | # puts "turned off" 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /include/compiler.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_COMPILE_H_ 2 | #define PICORBC_COMPILE_H_ 3 | 4 | #include 5 | 6 | #include "scope.h" 7 | #include "stream.h" 8 | #include "parse_header.h" 9 | #include "context.h" 10 | 11 | bool Compiler_compile(ParserState *p, StreamInterface *si, picorbc_context *cxt); 12 | 13 | ParserState *Compiler_parseInitState(ParserState *p, uint8_t node_box_size); 14 | 15 | void Compiler_parserStateFree(ParserState *p); 16 | 17 | #endif /* PICORBC_COMPILE_H_*/ 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mruby-pico-compiler [![build](https://github.com/hasumikin/mruby-pico-compiler/actions/workflows/ci.yml/badge.svg)](https://github.com/hasumikin/mruby-pico-compiler/actions/workflows/ci.yml) 2 | PicoCompiler class 3 | ## install by mrbgems 4 | - add conf.gem line to `build_config.rb` 5 | 6 | ```ruby 7 | MRuby::Build.new do |conf| 8 | 9 | # ... (snip) ... 10 | 11 | conf.gem :github => 'picoruby/mruby-pico-compiler' 12 | end 13 | ``` 14 | 15 | ## License 16 | under the MIT License: 17 | - see LICENSE file 18 | -------------------------------------------------------------------------------- /test/scall_test.rb: -------------------------------------------------------------------------------- 1 | class ScallTest < PicoRubyTest 2 | 3 | desc "&. oprator" 4 | assert_equal(<<~RUBY, "0\nnil\nfalse\nfalse") 5 | def a(v) 6 | case v 7 | when 0 8 | nil 9 | when 1 10 | true 11 | else 12 | false 13 | end 14 | end 15 | p a(0).to_i 16 | p a(0)&.to_i 17 | puts a(2).to_s 18 | puts a(2)&.to_s 19 | RUBY 20 | 21 | desc "&. oprator with block" 22 | assert_equal(<<~RUBY, "0\n1") 23 | [0,1]&.each do |i| 24 | puts i 25 | end 26 | RUBY 27 | end 28 | -------------------------------------------------------------------------------- /include/my_regex.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_REGEX_H_ 2 | #define PICORBC_REGEX_H_ 3 | 4 | #include 5 | #include "regex.h" 6 | 7 | #define REGEX_MAX_RESULT_NUM 2 8 | #define REGEX_MAX_RESULT_LENGTH 255 9 | 10 | typedef struct regex_result 11 | { 12 | char value[REGEX_MAX_RESULT_LENGTH]; 13 | } RegexResult; 14 | 15 | bool Regex_match2(char *str, const char *pattern); 16 | 17 | bool Regex_match3(char *str, const char *pattern, RegexResult result[REGEX_MAX_RESULT_NUM]); 18 | 19 | void MyRegex_setup(bool use_global_preg_cache); 20 | 21 | void MyRegexCache_free(void); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /test/op_assign_test.rb: -------------------------------------------------------------------------------- 1 | class OpAssignTest < PicoRubyTest 2 | desc "OP_ASSIGN with both []= and attr=" 3 | assert_equal(<<~RUBY, "[4, 6]") 4 | class Array 5 | def first 6 | self[0] 7 | end 8 | def first=(val) 9 | self[0] = val 10 | end 11 | end 12 | ary = [1, 2] 13 | ary.first += 3 14 | ary[1] *= 3 15 | p ary 16 | RUBY 17 | 18 | desc "<<=" 19 | assert_equal(<<~RUBY, "16") 20 | a = 2 21 | a <<= 3 22 | p a 23 | RUBY 24 | 25 | desc "return value" 26 | assert_equal(<<~RUBY, "1") 27 | a = {} 28 | p(a[:a] ||= 1) 29 | RUBY 30 | end 31 | 32 | -------------------------------------------------------------------------------- /test/rest_arg_test.rb: -------------------------------------------------------------------------------- 1 | class RestArgTest < PicoRubyTest 2 | 3 | desc "mruby/c should handle restarg" 4 | assert_equal(<<~RUBY, "1\n[2, 3, 4]") 5 | def a(m, *rest) 6 | p m, rest 7 | end 8 | a(1,2,3,4) 9 | RUBY 10 | 11 | desc "restarg in block" 12 | assert_equal(<<~RUBY, "[0]\n[1]") 13 | [0,1].each{|*v| p v} 14 | RUBY 15 | 16 | if @@vm_select == :mruby 17 | desc "restarg in mruby (mruby/c doesn't support m2 args)" 18 | assert_equal(<<~RUBY, "1\n[2, 3]\n4") 19 | def a(m, *rest, m2) 20 | p m, rest, m2 21 | end 22 | a(1,2,3,4) 23 | RUBY 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /test/string_test.rb: -------------------------------------------------------------------------------- 1 | class StringTest < PicoRubyTest 2 | desc "Keyword in str" 3 | assert_equal(<<~RUBY, "nil") 4 | puts "nil" 5 | RUBY 6 | 7 | desc "%q" 8 | assert_equal(<<~'RUBY', "a!b") 9 | puts %q!a\!b! 10 | RUBY 11 | 12 | desc "%Q" 13 | assert_equal(<<~'RUBY', "a!9b") 14 | puts %Q!a\!#{3 ** 2}b! 15 | RUBY 16 | 17 | desc "null letter strip!" 18 | assert_equal(<<~'RUBY', "") 19 | s = "\0" 20 | puts s.strip! 21 | RUBY 22 | 23 | desc "getter [x, y]" 24 | assert_equal(<<~'RUBY', "") 25 | s = "bar" 26 | puts s[2, 0] 27 | RUBY 28 | 29 | desc "binary" 30 | assert_equal(<<~'RUBY', "1") 31 | p "\x00".size 32 | RUBY 33 | 34 | end 35 | -------------------------------------------------------------------------------- /test/method_test.rb: -------------------------------------------------------------------------------- 1 | class MethodTest < PicoRubyTest 2 | desc "Mysterious case. Differnt from `p()` *space*" 3 | assert_equal(<<~RUBY, "nil") 4 | p () 5 | RUBY 6 | 7 | desc "Another *space* case" 8 | assert_equal(<<~RUBY, "1") 9 | p (1) do end 10 | RUBY 11 | 12 | desc "Integer class" 13 | assert_equal(<<~RUBY, "1234") 14 | puts 1234.to_s 15 | RUBY 16 | 17 | desc "Method chain" 18 | assert_equal(<<~RUBY, "1234") 19 | puts 1234.to_s.to_i 20 | RUBY 21 | 22 | desc "SCALL" 23 | assert_equal(<<~RUBY, "1") 24 | def my_block(&b) 25 | @cb = b 26 | end 27 | self.my_block do |v| 28 | p v 29 | end 30 | @cb&.call(1) 31 | RUBY 32 | end 33 | -------------------------------------------------------------------------------- /include/node.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_NODE_H_ 2 | #define PICORBC_NODE_H_ 3 | 4 | #include 5 | #include 6 | #include "parse_header.h" 7 | #ifndef PICORBC_PTR_SIZE 8 | #include 9 | #endif 10 | 11 | const char *Node_valueName(Node *self); 12 | 13 | void Node_setValue(Node *self, const char *s); 14 | 15 | bool Node_isAtom(Node *self); 16 | 17 | bool Node_isCons(Node *self); 18 | 19 | bool Node_isLiteral(Node *self); 20 | 21 | AtomType Node_atomType(Node *self); 22 | 23 | const char *Node_literalName(Node *self); 24 | 25 | NodeBox *Node_newBox(ParserState *p); 26 | 27 | Node *Node_new(ParserState *p); 28 | 29 | void Node_freeAllNode(NodeBox *box); 30 | 31 | void freeNode(Node *n); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /include/stream.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_FMEMSTREAM_H_ 2 | #define PICORBC_FMEMSTREAM_H_ 3 | 4 | typedef enum stream_type 5 | { 6 | STREAM_TYPE_FILE, 7 | STREAM_TYPE_MEMORY 8 | } StreamType; 9 | 10 | typedef struct stream_interface 11 | { 12 | StreamType type; 13 | uint8_t node_box_size; 14 | void *stream; 15 | char *(*fgetsProc)(char *s, int n, FILE *stream); 16 | int (*feofProc)(FILE *stream); 17 | } StreamInterface; 18 | 19 | typedef struct fmemstream 20 | { 21 | int pos; 22 | const char *mem; 23 | int size; 24 | } fmemstream; 25 | 26 | StreamInterface *StreamInterface_new(FILE *fp, const char *c, StreamType st); 27 | 28 | void StreamInterface_free(StreamInterface *si); 29 | 30 | #endif /* PICORBC_FMEMSTREAM_H_ */ 31 | -------------------------------------------------------------------------------- /test/function_test.rb: -------------------------------------------------------------------------------- 1 | class FunctionTest < PicoRubyTest 2 | desc "One arg without paren" 3 | assert_equal(<<~RUBY, "Hello") 4 | puts "Hello" 5 | RUBY 6 | 7 | desc "One arg with paren" 8 | assert_equal(<<~RUBY, "Hello") 9 | puts("Hello") 10 | RUBY 11 | 12 | desc "Multiple args without paren" 13 | assert_equal(<<~RUBY, "String\nsymbol\n1\n3.14") 14 | puts "String", :symbol, 1, 3.14 15 | RUBY 16 | 17 | desc "Multiple args with paren" 18 | assert_equal(<<~RUBY, "String\nsymbol\n1\n3.14") 19 | puts("String", :symbol, 1, 3.14) 20 | RUBY 21 | 22 | desc "p p p" 23 | assert_equal(<<~RUBY, "nil\nnil") 24 | p p p 25 | RUBY 26 | 27 | desc "p p p 0" 28 | assert_equal(<<~RUBY, "0\n0\n0") 29 | p p p 0 30 | RUBY 31 | end 32 | -------------------------------------------------------------------------------- /test/assign_test.rb: -------------------------------------------------------------------------------- 1 | class AssignTest < PicoRubyTest 2 | assert_equal(<<~RUBY, "1") 3 | a = 1 4 | puts a 5 | RUBY 6 | 7 | assert_equal(<<~RUBY, '"hello"') 8 | a = b = "hello" 9 | p b 10 | RUBY 11 | 12 | assert_equal(<<~RUBY, '"hello"') 13 | a = b = "hello" 14 | p a 15 | RUBY 16 | 17 | assert_equal(<<~RUBY, "4\n2\n2\n2") 18 | a = 1 19 | 1.times do 20 | a += 3 21 | p a 22 | b = a = 2 23 | p a, b 24 | end 25 | p a 26 | RUBY 27 | 28 | assert_equal(<<~RUBY, "1") 29 | class A 30 | attr_accessor :b 31 | end 32 | a = A.new 33 | a.b = 1 34 | puts a.b 35 | RUBY 36 | 37 | assert_equal(<<~RUBY, "[2, 1]") 38 | a = [0, 1] 39 | a[0] = 2 40 | p a 41 | RUBY 42 | end 43 | -------------------------------------------------------------------------------- /test/ivar_test.rb: -------------------------------------------------------------------------------- 1 | class IvarTest < PicoRubyTest 2 | desc "assign" 3 | assert_equal(<<~RUBY, '[0]') 4 | @ivar = [0] 5 | p @ivar 6 | RUBY 7 | 8 | desc "init idiom" 9 | assert_equal(<<~RUBY, '"init"') 10 | @ivar ||= "init" 11 | p @ivar 12 | RUBY 13 | 14 | desc "op_assign" 15 | assert_equal(<<~RUBY, '2') 16 | @ivar = 1 17 | @ivar += 1 18 | p @ivar 19 | RUBY 20 | 21 | desc "op_assign array" 22 | assert_equal(<<~RUBY, '[]') 23 | @ivar||=[] 24 | p @ivar 25 | RUBY 26 | 27 | desc "op_assign hash" 28 | assert_equal(<<~RUBY, '{}') 29 | @ivar||={} 30 | p @ivar 31 | RUBY 32 | 33 | desc "op_assign hash 2" 34 | assert_equal(<<~RUBY, '{:a=>1}') 35 | @ivar={} 36 | @ivar[:a] ||= 1 37 | p @ivar 38 | RUBY 39 | end 40 | -------------------------------------------------------------------------------- /test/gvar_test.rb: -------------------------------------------------------------------------------- 1 | class GvarTest < PicoRubyTest 2 | desc "assign" 3 | assert_equal(<<~RUBY, '[0]') 4 | $gvar = [0] 5 | p $gvar 6 | RUBY 7 | 8 | desc "init idiom" 9 | assert_equal(<<~RUBY, '"init"') 10 | $gvar ||= "init" 11 | p $gvar 12 | RUBY 13 | 14 | desc "op_assign" 15 | assert_equal(<<~RUBY, '2') 16 | $gvar = 1 17 | $gvar += 1 18 | p $gvar 19 | RUBY 20 | 21 | desc "op_assign array" 22 | assert_equal(<<~RUBY, '[]') 23 | $gvar||=[] 24 | p $gvar 25 | RUBY 26 | 27 | desc "gvar op_assign hash" 28 | assert_equal(<<~RUBY, '{}') 29 | $gvar||={} 30 | p $gvar 31 | RUBY 32 | 33 | desc "op_assign hash 2" 34 | assert_equal(<<~RUBY, '{:a=>1}') 35 | $gvar={} 36 | $gvar[:a] ||= 1 37 | p $gvar 38 | RUBY 39 | end 40 | -------------------------------------------------------------------------------- /test/class_test.rb: -------------------------------------------------------------------------------- 1 | class ClassTest < PicoRubyTest 2 | desc "re-define Array#collect" 3 | assert_equal(<<~RUBY, "[\"1\", \"2\"]") 4 | def collect 5 | i = 0 6 | ary = [] 7 | while i < length 8 | ary[i] = yield self[i] 9 | i += 1 10 | end 11 | return ary 12 | end 13 | p [1, 2].collect {|e| e.to_s } 14 | RUBY 15 | 16 | desc "inheritance from nested class" 17 | assert_equal(<<~RUBY, "4\n6") 18 | class BLE 19 | class AttServer 20 | def get_packet(i) 21 | puts i * 2 22 | end 23 | end 24 | end 25 | class MyServer < BLE::AttServer 26 | def get_packet(i) 27 | puts i * 3 28 | end 29 | end 30 | BLE::AttServer.new.get_packet(2) 31 | MyServer.new.get_packet(2) 32 | RUBY 33 | end 34 | -------------------------------------------------------------------------------- /include/dump.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_DUMP_H_ 2 | #define PICORBC_DUMP_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "opcode.h" 8 | #include "scope.h" 9 | 10 | #define PICORB_DUMP_DEBUG_INFO 1 11 | #define PICORB_DUMP_STATIC 2 12 | 13 | #define PICORB_DUMP_OK 0 14 | #define PICORB_DUMP_GENERAL_FAILURE (-1) 15 | #define PICORB_DUMP_WRITE_FAULT (-2) 16 | #define PICORB_DUMP_READ_FAULT (-3) 17 | #define PICORB_DUMP_INVALID_FILE_HEADER (-4) 18 | #define PICORB_DUMP_INVALID_IREP (-5) 19 | #define PICORB_DUMP_INVALID_ARGUMENT (-6) 20 | 21 | void Dump_hexDump(FILE *fp, uint8_t *irep); 22 | 23 | int Dump_mrbDump(FILE *fp, Scope *scope, const char *initname); 24 | 25 | int Dump_cstructDump(FILE *fp, Scope *scope, uint8_t flags, const char *initname); 26 | 27 | #endif /* PICORBC_DUMP_H_ */ 28 | -------------------------------------------------------------------------------- /test/singleton_test.rb: -------------------------------------------------------------------------------- 1 | class SingletonTest < PicoRubyTest 2 | 3 | if @@vm_select == :mruby 4 | 5 | desc "singleton self" 6 | assert_equal(<<~RUBY, ":hello") 7 | def self.a(v) 8 | p v 9 | end 10 | self.a(:hello) 11 | RUBY 12 | 13 | desc "singleton lvar" 14 | assert_equal(<<~RUBY, ":hello_world") 15 | lvar = String.new 16 | def lvar.a(v) 17 | p v 18 | end 19 | lvar.a(:hello_world) 20 | RUBY 21 | 22 | desc "singleton Klass" 23 | assert_equal(<<~RUBY, ":hello_hello") 24 | def Array.a(v) 25 | p v 26 | end 27 | Array.a(:hello_hello) 28 | RUBY 29 | 30 | desc "singleton Klass" 31 | assert_equal(<<~RUBY, "heyheyhey") 32 | class << String 33 | def a(n) 34 | puts "hey" * n 35 | end 36 | end 37 | String.a(3) 38 | RUBY 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /test/super_test.rb: -------------------------------------------------------------------------------- 1 | class SuperTest < PicoRubyTest 2 | 3 | desc "super 1" 4 | assert_equal(<<~'RUBY', "1\n2") 5 | class A 6 | def a(v=0) 7 | 1 + v 8 | end 9 | end 10 | class B < A 11 | def a 12 | super 13 | end 14 | end 15 | class C < A 16 | def a 17 | super(1) 18 | end 19 | end 20 | p B.new.a 21 | p C.new.a 22 | RUBY 23 | 24 | desc "super with block" 25 | assert_equal(<<~RUBY, "1\n2\n4") 26 | class A 27 | def a(v, &b) 28 | yield(v) 29 | end 30 | end 31 | A.new.a(1){|v| p v} 32 | class B < A 33 | def a(v, &b) 34 | super 35 | end 36 | end 37 | B.new.a(2){|v| p v} 38 | class C < A 39 | def a(v, &b) 40 | super(v) do |v| 41 | p v + 1 42 | end 43 | end 44 | end 45 | C.new.a(3) 46 | RUBY 47 | 48 | end 49 | -------------------------------------------------------------------------------- /include/picorbc.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_PICORBC_H_ 2 | #define PICORBC_PICORBC_H_ 3 | 4 | /* 5 | * banned.h occurs errors like 6 | * "error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token" 7 | * because of banned.h 8 | * If PICORUBY_DEBUG is not defined, CFLAGS will contain "-O0" which can avoid the error above. 9 | * See picoruby/build_config/default.rb 10 | */ 11 | #ifdef PICORUBY_DEBUG 12 | #include "banned.h" 13 | #endif 14 | 15 | #include "version.h" 16 | #include "debug.h" 17 | #include "common.h" 18 | #include "tokenizer.h" 19 | #include "generator.h" 20 | #include "scope.h" 21 | #include "compiler.h" 22 | #include "stream.h" 23 | #include "parse.h" 24 | 25 | #ifndef PICORUBY_ARRAY_SPLIT_COUNT 26 | #define PICORUBY_ARRAY_SPLIT_COUNT 64 27 | #endif 28 | 29 | #ifndef PICORUBY_HASH_SPLIT_COUNT 30 | #define PICORUBY_HASH_SPLIT_COUNT 32 31 | #endif 32 | 33 | #endif /* PICORBC_PICORBC_H_ */ 34 | -------------------------------------------------------------------------------- /lib/ptr_size_generator.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Deprecated on Nov 18 2022 3 | * This code is not used anymore 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) 10 | // C99 or newer 11 | static const char format[] = "echo '#define PICORBC_PTR_SIZE %zu' > ptr_size.h"; 12 | #define put_ptr_size(type) sprintf(command, format, sizeof(type)) 13 | 14 | #elif defined(_MSC_VER) 15 | // Visual C/C++ 16 | static const char format[] = "echo '#define PICORBC_PTR_SIZE %Iu' > ptr_size.h"; 17 | #define put_ptr_size(type) sprintf(command, format, sizeof(type)) 18 | 19 | #else 20 | static const char format[] = "echo '#define PICORBC_PTR_SIZE %lu' > ptr_size.h"; 21 | #define put_ptr_size(type) sprintf(command, format, (unsigned long)sizeof(type)) 22 | #endif 23 | 24 | int main(void) 25 | { 26 | char command[100]; 27 | put_ptr_size(void *); 28 | return system(command); 29 | } 30 | -------------------------------------------------------------------------------- /test/fixtures/primary.rb: -------------------------------------------------------------------------------- 1 | debugprint('start', 'main_loop') 2 | 3 | $co2 = Co2.new 4 | $thermistor = Thermistor.new 5 | #$wifi = Wifi.new 6 | 7 | led = Led.new(19) 8 | 9 | while true 10 | co2 = $co2.concentrate 11 | temperature = $thermistor.temperature 12 | puts "CO2: #{co2}, Temperature: #{temperature}" 13 | if co2 > 2000 14 | 20.times do 15 | led.turn_on 16 | sleep 0.1 17 | led.turn_off 18 | sleep 0.1 19 | end 20 | elsif co2 > 1500 21 | led.turn_off 22 | sleep 4.2 23 | led.turn_on 24 | 4.times do 25 | led.turn_off 26 | sleep 0.1 27 | led.turn_on 28 | sleep 0.1 29 | end 30 | elsif co2 > 1000 31 | led.turn_off 32 | sleep 4.6 33 | 2.times do 34 | led.turn_off 35 | sleep 0.1 36 | led.turn_on 37 | sleep 0.1 38 | end 39 | else 40 | led.turn_off 41 | sleep 4.9 42 | led.turn_on 43 | sleep 0.1 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /include/tokenizer.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_TOKENIZER_H_ 2 | #define PICORBC_TOKENIZER_H_ 3 | 4 | #include 5 | 6 | #include "parse_header.h" 7 | #include "token.h" 8 | #include "stream.h" 9 | 10 | typedef enum mode 11 | { 12 | MODE_NONE, 13 | MODE_COMMENT, 14 | MODE_QWORDS, 15 | MODE_WORDS, 16 | MODE_QSYMBOLS, 17 | MODE_SYMBOLS, 18 | MODE_TSTRING_DOUBLE, 19 | MODE_TSTRING_SINGLE, 20 | } Mode; 21 | 22 | typedef struct tokenizer 23 | { 24 | Mode mode; 25 | char *line; 26 | StreamInterface *si; 27 | Token *currentToken; 28 | int line_num; 29 | int pos; 30 | char modeTerminater; 31 | } Tokenizer; 32 | 33 | Tokenizer* const Tokenizer_new(ParserState *p, StreamInterface *si); 34 | 35 | void Tokenizer_free(Tokenizer *self); 36 | 37 | void Tokenizer_puts(Tokenizer* const self, char *line); 38 | 39 | bool Tokenizer_hasMoreTokens(Tokenizer* const self); 40 | 41 | int Tokenizer_advance(Tokenizer* const self, ParserState *p, bool recursive); 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /test/splat_test.rb: -------------------------------------------------------------------------------- 1 | class SplatTest < PicoRubyTest 2 | 3 | if @@vm_select == :mruby 4 | 5 | desc "splat in fcall 1" 6 | assert_equal(<<~RUBY, "1") 7 | p(*1) 8 | RUBY 9 | 10 | desc "splat in fcall 2" 11 | assert_equal(<<~RUBY, "1\n2") 12 | p(1, *2) 13 | RUBY 14 | 15 | desc "splat in fcall 3" 16 | assert_equal(<<~RUBY, "[3, 4]\n1\n2\n[3, 4]") 17 | p(*1,2,p([3,4])) 18 | RUBY 19 | 20 | desc "splat in fcall 4" 21 | assert_equal(<<~RUBY, "3\n4\n1\n2\n[3, 4]") 22 | p(*1,2,p(*[3,4])) 23 | RUBY 24 | 25 | desc "splat in fcall 5" 26 | assert_equal(<<~RUBY, "[\"str\"]") 27 | a = *"str" 28 | p a 29 | RUBY 30 | 31 | desc "splat in fcall 6" 32 | assert_equal(<<~RUBY, "1\n2\n3") 33 | p(1,2,*3) 34 | RUBY 35 | 36 | desc "splat in fcall 7" 37 | assert_equal(<<~RUBY, "hey") 38 | case 0 39 | when *[0] 40 | puts "hey" 41 | end 42 | RUBY 43 | 44 | end 45 | 46 | end 47 | 48 | -------------------------------------------------------------------------------- /src/token.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | Token *Token_new(void) 11 | { 12 | Token *self = (Token *)picorbc_alloc(sizeof(Token)); 13 | self->value = NULL; 14 | self->type = ON_NONE; 15 | self->prev = NULL; 16 | self->next = NULL; 17 | self->refCount = 1; 18 | return self; 19 | } 20 | 21 | void Token_free(Token* self) 22 | { 23 | if (self->value != NULL) { 24 | picorbc_free(self->value); 25 | DEBUGP("free Token->value: `%s`", self->value); 26 | } 27 | DEBUGP("free Token: %p", self); 28 | picorbc_free(self); 29 | } 30 | 31 | bool Token_exists(Token* const self) 32 | { 33 | if (strlen(self->value) > 0) 34 | return true; 35 | return false; 36 | } 37 | 38 | void Token_GC(Token* token) 39 | { 40 | if (token == NULL || token->refCount > 0) return; 41 | Token_GC(token->prev); 42 | token->next->prev = NULL; 43 | Token_free(token); 44 | } 45 | -------------------------------------------------------------------------------- /test/namespace_test.rb: -------------------------------------------------------------------------------- 1 | class NamespaceTest < PicoRubyTest 2 | 3 | if @@vm_select == :mruby 4 | 5 | desc "name space 1" 6 | assert_equal(<<~RUBY, ":foo") 7 | module A 8 | module B 9 | FOO = :foo 10 | end 11 | end 12 | p A::B::FOO 13 | RUBY 14 | 15 | desc "name space 2" 16 | assert_equal(<<~RUBY, ":foo") 17 | module A 18 | end 19 | module A::B 20 | FOO = :foo 21 | end 22 | p A::B::FOO 23 | RUBY 24 | 25 | desc "name space 3" 26 | assert_equal(<<~RUBY, ":foo") 27 | module A 28 | class B 29 | FOO = :foo 30 | end 31 | end 32 | p A::B::FOO 33 | RUBY 34 | 35 | desc "name space fail" 36 | assert_equal(<<~RUBY, "NameError") 37 | module A 38 | module B 39 | FOO = :foo 40 | end 41 | end 42 | begin 43 | A::FOO 44 | rescue => e 45 | puts e.class 46 | end 47 | RUBY 48 | 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /include/token_data.h: -------------------------------------------------------------------------------- 1 | static struct { 2 | const char *string; 3 | } OPERATORS_2[] = { 4 | {"**"}, 5 | {"*="}, 6 | {"<<"}, 7 | {"<="}, 8 | {">>"}, 9 | {">="}, 10 | {"&&"}, 11 | {"&="}, 12 | {"||"}, 13 | {"|="}, 14 | {"=="}, 15 | {"=>"}, 16 | {"=~"}, 17 | {"!="}, 18 | {"!~"}, 19 | {".."}, 20 | {"::"}, 21 | {"+="}, 22 | {"-="}, 23 | {"/="}, 24 | {"%="}, 25 | {"^="}, 26 | {NULL} 27 | }; 28 | 29 | static struct { 30 | const char *string; 31 | } OPERATORS_3[] = { 32 | {"<=>"}, 33 | {"==="}, 34 | {"..."}, 35 | {"**="}, 36 | {"<<="}, 37 | {">>="}, 38 | {"&&="}, 39 | {"||="}, 40 | {NULL} 41 | }; 42 | 43 | static struct { 44 | const char letter; 45 | } PARENS[] = { 46 | {'('}, 47 | {')'}, 48 | {'['}, 49 | {']'}, 50 | {'{'}, 51 | {'}'}, 52 | {0} 53 | }; 54 | 55 | static struct { 56 | char letter; 57 | } COMMAS[] = { 58 | {','}, 59 | {0} 60 | }; 61 | 62 | static struct { 63 | char letter; 64 | } SEMICOLONS[] = { 65 | {';'}, 66 | {0} 67 | }; 68 | 69 | -------------------------------------------------------------------------------- /include/context.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_CONTEXT_H_ 2 | #define PICORBC_CONTEXT_H_ 3 | 4 | #include 5 | 6 | typedef uint8_t mrb_bool; 7 | typedef uint32_t mrb_sym; 8 | 9 | struct mrb_parser_state; 10 | 11 | /* from compile.h */ 12 | typedef struct mrbc_context { 13 | mrb_sym *syms; 14 | int slen; 15 | char *filename; 16 | uint16_t lineno; 17 | int (*partial_hook)(struct mrb_parser_state*); 18 | void *partial_data; 19 | struct RClass *target_class; 20 | mrb_bool capture_errors:1; 21 | mrb_bool dump_result:1; 22 | mrb_bool no_exec:1; 23 | mrb_bool keep_lv:1; 24 | mrb_bool no_optimize:1; 25 | const struct RProc *upper; 26 | 27 | size_t parser_nerr; 28 | } mrbc_context; 29 | 30 | typedef mrbc_context picorbc_context; 31 | 32 | picorbc_context* picorbc_context_new(picorbc_context *cxt); 33 | 34 | void picorbc_context_free(picorbc_context *cxt); 35 | 36 | void picorbc_cleanup_local_variables(picorbc_context *c); 37 | 38 | const char* picorbc_filename(picorbc_context *c, const char *s); 39 | 40 | #endif /* PICORBC_CONTEXT_H_*/ 41 | -------------------------------------------------------------------------------- /test/array_test.rb: -------------------------------------------------------------------------------- 1 | class ArrayTest < PicoRubyTest 2 | desc "array basic case" 3 | assert_equal(<<~RUBY, "[1, 2, 3, [4, 5, 6, 7, 8], 9, 10]") 4 | p([1,2,3,[4,5,6,7,8],9,10]) 5 | RUBY 6 | 7 | desc "PICORUBY_ARRAY_SPLIT_COUNT case" 8 | assert_equal(<<~RUBY, ":A63\n:A64\n:A65\n:A66\n70") 9 | ary = %i( 10 | A00 A01 A02 A03 A04 A05 A06 A07 A08 A09 11 | A10 A11 A12 A13 A14 A15 A16 A17 A18 A19 12 | A20 A21 A22 A23 A24 A25 A26 A27 A28 A29 13 | A30 A31 A32 A33 A34 A35 A36 A37 A38 A39 14 | A40 A41 A42 A43 A44 A45 A46 A47 A48 A49 15 | A50 A51 A52 A53 A54 A55 A56 A57 A58 A59 16 | A60 A61 A62 A63 A64 A65 A66 A67 A68 A69 17 | ) 18 | p ary[63] 19 | p ary[64] 20 | p ary[65] 21 | p ary[66] 22 | puts ary.size 23 | RUBY 24 | 25 | desc "getter [x,y]" 26 | assert_equal(<<~RUBY, "[2, 3]") 27 | ary = [0,1,2,3] 28 | p ary[2, 3] 29 | RUBY 30 | 31 | desc "setter [x,y]" 32 | assert_equal(<<~RUBY, "[0, 1, 2, 3, 44]") 33 | ary = [0,1,2,3] 34 | ary[4, 10] = 44 35 | p ary 36 | RUBY 37 | end 38 | -------------------------------------------------------------------------------- /include/regex.h: -------------------------------------------------------------------------------- 1 | #ifndef REGEX_LIGHT_H_ 2 | #define REGEX_LIGHT_H_ 3 | 4 | #include 5 | 6 | typedef struct re_atom ReAtom; 7 | 8 | typedef struct { 9 | size_t re_nsub; // number of parenthesized subexpressions ( ) 10 | ReAtom *atoms; 11 | } regex_t; 12 | 13 | typedef struct { 14 | int16_t rm_so; // start position of match 15 | int16_t rm_eo; // end position of match 16 | } regmatch_t; 17 | 18 | /* regcomp() flags */ 19 | #define REG_BASIC 0000 20 | #define REG_EXTENDED 0001 21 | #define REG_ICASE 0002 22 | #define REG_NOSUB 0004 23 | #define REG_NEWLINE 0010 24 | #define REG_NOSPEC 0020 25 | #define REG_PEND 0040 26 | #define REG_DUMP 0200 27 | 28 | int regcomp(regex_t *preg, const char *pattern, int _cflags); 29 | void regfree(regex_t *preg); 30 | int regexec(regex_t *preg, const char *string, size_t nmatch, regmatch_t *pmatch, int eflags); 31 | 32 | #ifndef REGEX_USE_ALLOC_LIBC 33 | void RegexSetAllocProcs(void *(*mallocProc)(size_t), void (*freeProc)(void *)); 34 | #endif 35 | 36 | #endif /* !REGEX_LIGHT_H_ */ 37 | -------------------------------------------------------------------------------- /test/yield_test.rb: -------------------------------------------------------------------------------- 1 | class YieldTest < PicoRubyTest 2 | desc "do block 1" 3 | assert_equal(<<~'RUBY', "Hello Ruby") 4 | def my_method(m) 5 | yield m 6 | end 7 | my_method("Ruby") do |v| 8 | puts "Hello #{v}" 9 | end 10 | RUBY 11 | 12 | desc "do block 2" 13 | assert_equal(<<~'RUBY', "Hello PicoRuby") 14 | def my_method(m, n) 15 | yield m, n 16 | end 17 | my_method("Ruby", "Pico") do |v, x| 18 | puts "Hello #{x}#{v}" 19 | end 20 | RUBY 21 | 22 | desc "bang yield self" 23 | assert_equal(<<~'RUBY', "false") 24 | def my_method 25 | puts false if ! yield self 26 | end 27 | my_method do 28 | false 29 | end 30 | RUBY 31 | 32 | desc "yield in nested irep" 33 | assert_equal(<<~'RUBY', "Hello Ruby") 34 | def exec(sql, bind_vars = []) 35 | bind_vars.each do |v| 36 | yield v 37 | end 38 | end 39 | result = [] 40 | exec("insert into test (a, b) values (?, ?)", ["Hello", "Ruby"]) do |v| 41 | result << v 42 | end 43 | puts result.join(" ") 44 | RUBY 45 | end 46 | -------------------------------------------------------------------------------- /test/integer_test.rb: -------------------------------------------------------------------------------- 1 | class IntegerTest < PicoRubyTest 2 | assert_equal(<<~RUBY, "255") 3 | p(0b11111111) 4 | RUBY 5 | 6 | assert_equal(<<~RUBY, "65535") 7 | p(0b1111111111111111) 8 | RUBY 9 | 10 | assert_equal(<<~RUBY, "131071") 11 | p(0b11111111111111111) 12 | RUBY 13 | 14 | assert_equal(<<~RUBY, "-131071") 15 | p(-0b11111111111111111) 16 | RUBY 17 | 18 | assert_equal(<<~RUBY, "2097151") 19 | p(0o7777777) 20 | RUBY 21 | 22 | assert_equal(<<~RUBY, "-2097151") 23 | p(-0o7777777) 24 | RUBY 25 | 26 | assert_equal(<<~RUBY, "65535") 27 | p(0xFFFF) 28 | RUBY 29 | 30 | assert_equal(<<~RUBY, "1048575") 31 | p(0xFFFFF) 32 | RUBY 33 | 34 | assert_equal(<<~RUBY, "-1048575") 35 | p(-0xFFFFF) 36 | RUBY 37 | 38 | assert_equal(<<~RUBY, "70000") 39 | p(-70000.abs) 40 | RUBY 41 | 42 | assert_equal(<<~RUBY, "7000") 43 | p(7000.001.to_i) 44 | RUBY 45 | 46 | assert_equal(<<~RUBY, "-7000") 47 | p(-7000.001.to_i) 48 | RUBY 49 | 50 | desc "0755 is eq tp 0o755" 51 | assert_equal(<<~RUBY, "493") 52 | p 0755 53 | RUBY 54 | end 55 | -------------------------------------------------------------------------------- /include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_COMMON_H_ 2 | #define PICORBC_COMMON_H_ 3 | 4 | #include 5 | 6 | #include "debug.h" 7 | 8 | #ifdef MRBC_ALLOC_LIBC 9 | #include 10 | #define PICORBC_ALLOC(size) malloc(size) 11 | #define PICORBC_FREE(ptr) free(ptr) 12 | #else 13 | #include 14 | #define PICORBC_ALLOC(size) mrbc_raw_alloc(size) 15 | #define PICORBC_FREE(ptr) mrbc_raw_free(ptr) 16 | #endif /* MRBC_ALLOC_LIBC */ 17 | 18 | #define MAX_LINE_LENGTH 256 19 | 20 | #define MAX_TOKEN_LENGTH 256 21 | 22 | #ifdef PICORUBY_DEBUG 23 | typedef struct alloc_list 24 | { 25 | int count; 26 | void *ptr; 27 | struct alloc_list *prev; 28 | struct alloc_list *next; 29 | } AllocList; 30 | 31 | void print_memory(void); 32 | 33 | void memcheck(void); 34 | #endif /* !PICORUBY_DEBUG */ 35 | 36 | void *picorbc_alloc(size_t size); 37 | 38 | void picorbc_free(void *ptr); 39 | 40 | char *strsafecpy(char *str1, const char *str2, size_t max); 41 | 42 | char *strsafencpy(char *s1, const char *s2, size_t n, size_t max); 43 | 44 | char *strsafecat(char *dst, const char *src, size_t max); 45 | 46 | #endif /* PICORBC_COMMON_H_ */ 47 | -------------------------------------------------------------------------------- /src/context.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include "common.h" 6 | #include "scope.h" 7 | 8 | picorbc_context* 9 | picorbc_context_new(picorbc_context *cxt) 10 | { 11 | if (cxt == NULL) { 12 | cxt = (picorbc_context *)picorbc_alloc(sizeof(picorbc_context)); 13 | } 14 | memset(cxt, 0, sizeof(picorbc_context)); 15 | return cxt; 16 | } 17 | 18 | void 19 | picorbc_context_free(picorbc_context *cxt) 20 | { 21 | picorbc_free(cxt->filename); 22 | Scope_freeLvar((Lvar *)cxt->syms); 23 | picorbc_free(cxt); 24 | } 25 | 26 | void 27 | picorbc_cleanup_local_variables(picorbc_context *cxt) 28 | { 29 | if (cxt->syms) { 30 | Scope_freeLvar((Lvar *)cxt->syms); 31 | cxt->syms = NULL; 32 | cxt->slen = 0; 33 | } 34 | } 35 | 36 | const char* 37 | picorbc_filename(picorbc_context *cxt, const char *s) 38 | { 39 | if (s) { 40 | size_t len = strlen(s); 41 | char *p = (char *)picorbc_alloc(len + 1); 42 | 43 | memcpy(p, s, len + 1); 44 | if (cxt->filename) { 45 | picorbc_free(cxt->filename); 46 | } 47 | cxt->filename = p; 48 | } 49 | return cxt->filename; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /test/case_test.rb: -------------------------------------------------------------------------------- 1 | class CaseTest < PicoRubyTest 2 | desc "case 1" 3 | assert_equal(<<~RUBY, "true") 4 | dummy = [0,1,2] 5 | res = case dummy[1] 6 | when 1 7 | dummy[0] 8 | true 9 | when 2 10 | false 11 | end 12 | p res 13 | RUBY 14 | 15 | desc "case 2" 16 | assert_equal(<<~RUBY, "false") 17 | dummy = [0,1,2] 18 | res = case dummy[2] 19 | when 1 20 | true 21 | when 2 22 | dummy[0] 23 | false 24 | end 25 | p res 26 | RUBY 27 | 28 | desc "case no else" 29 | assert_equal(<<~RUBY, "nil") 30 | dummy = [0,1,2] 31 | res = case dummy[0] 32 | when 1 33 | true 34 | when 2 35 | false 36 | end 37 | p res 38 | RUBY 39 | 40 | desc "case else" 41 | assert_equal(<<~RUBY, ":ruby") 42 | dummy = [1] 43 | res = case dummy[0] 44 | when 2 45 | false 46 | else 47 | String.new 48 | [{a: 2}, "hello", :ruby][2] 49 | end 50 | p res 51 | RUBY 52 | 53 | desc "case expression as an argument" 54 | assert_equal(<<~RUBY, "true") 55 | p case 0 56 | when 0 57 | true 58 | end 59 | RUBY 60 | 61 | end 62 | -------------------------------------------------------------------------------- /include/value.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_VALUE_H_ 2 | #define PICORBC_VALUE_H_ 3 | 4 | #if defined _MSC_VER && _MSC_VER < 1800 5 | # define PRIo64 "llo" 6 | # define PRId64 "lld" 7 | # define PRIu64 "llu" 8 | # define PRIx64 "llx" 9 | # define PRIo16 "ho" 10 | # define PRId16 "hd" 11 | # define PRIu16 "hu" 12 | # define PRIx16 "hx" 13 | # define PRIo32 "o" 14 | # define PRId32 "d" 15 | # define PRIu32 "u" 16 | # define PRIx32 "x" 17 | #else 18 | # include 19 | #endif 20 | 21 | #if defined(MRBC_INT64) 22 | typedef int64_t mrb_int; 23 | typedef uint64_t mrb_uint; 24 | # define MRB_INT_BIT 64 25 | # define MRB_INT_MIN INT64_MIN 26 | # define MRB_INT_MAX INT64_MAX 27 | # define MRB_PRIo PRIo64 28 | # define MRB_PRId PRId64 29 | # define MRB_PRIx PRIx64 30 | #else 31 | typedef int32_t mrb_int; 32 | typedef uint32_t mrb_uint; 33 | # define MRB_INT_BIT 32 34 | # define MRB_INT_MIN INT32_MIN 35 | # define MRB_INT_MAX INT32_MAX 36 | # define MRB_PRIo PRIo32 37 | # define MRB_PRId PRId32 38 | # define MRB_PRIx PRIx32 39 | #endif 40 | 41 | #ifdef MRB_ENDIAN_BIG 42 | # define MRB_ENDIAN_LOHI(a,b) a b 43 | #else 44 | # define MRB_ENDIAN_LOHI(a,b) b a 45 | #endif 46 | #endif /* PICORBC_VALUE_H_ */ 47 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | MRUBY_CONFIG=File.expand_path(ENV["MRUBY_CONFIG"] || "build_config/host.rb") 2 | MRUBY_VERSION=ENV["MRUBY_VERSION"] || "3.0.0" 3 | 4 | file :mruby do 5 | sh "git clone --depth=1 https://github.com/mruby/mruby.git" 6 | if MRUBY_VERSION != 'master' 7 | Dir.chdir 'mruby' do 8 | sh "git fetch --tags" 9 | rev = %x{git rev-parse #{MRUBY_VERSION}} 10 | sh "git checkout #{rev}" 11 | end 12 | end 13 | end 14 | 15 | desc "compile binary" 16 | task :compile => :mruby do 17 | sh "cd mruby && rake all MRUBY_CONFIG=#{MRUBY_CONFIG}" 18 | end 19 | 20 | desc "test" 21 | task :test => :mruby do 22 | sh "cd mruby && rake all test MRUBY_CONFIG=#{MRUBY_CONFIG}" 23 | end 24 | 25 | desc "cleanup" 26 | task :clean do 27 | rm_rf "include/atom_helper.h" 28 | rm_rf "include/keyword_helper.h" 29 | rm_rf "include/token_helper.h" 30 | rm_rf "include/tokenizer_helper.h" 31 | rm_rf "include/parse.h" 32 | rm_rf "lib/lemon" 33 | rm_rf "src/parse.out" 34 | rm_rf "src/parse.c" 35 | rm_rf "src/parse.h" 36 | exit 0 unless File.directory?('mruby') 37 | sh "cd mruby && MRUBY_CONFIG="" rake deep_clean" 38 | rm_rf "mruby/bin/picorbc" 39 | end 40 | 41 | task :default => :compile 42 | -------------------------------------------------------------------------------- /include/generator.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_GENERATOR_H_ 2 | #define PICORBC_GENERATOR_H_ 3 | 4 | #include 5 | 6 | #include "scope.h" 7 | 8 | #define HEADER_SIZE 32 9 | #define FOOTER_SIZE 8 10 | 11 | typedef struct jmp_label 12 | { 13 | void *address; 14 | uint32_t pos; 15 | } JmpLabel; 16 | 17 | typedef struct backpatch 18 | { 19 | JmpLabel *label; 20 | struct backpatch *next; 21 | } Backpatch; 22 | 23 | typedef struct break_stack 24 | { 25 | void *point; 26 | struct break_stack *prev; 27 | uint32_t next_pos; 28 | uint32_t redo_pos; 29 | } BreakStack; 30 | 31 | typedef struct retry_stack 32 | { 33 | uint32_t pos; 34 | struct retry_stack *prev; 35 | } RetryStack; 36 | 37 | typedef struct generator_state 38 | { 39 | BreakStack *break_stack; 40 | RetryStack *retry_stack; 41 | Backpatch *backpatch; /* for backpatching of JMP label */ 42 | uint16_t nargs_added; 43 | uint16_t nargs_before_splat; 44 | uint16_t nargs_after_splat; 45 | uint8_t gen_splat_status; 46 | uint8_t gen_array_status; 47 | uint8_t gen_array_count; 48 | } GeneratorState; 49 | 50 | typedef struct node Node; 51 | 52 | void Generator_generate(Scope *scope, Node *root, bool verbose); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /test/fixtures/wifi.rb: -------------------------------------------------------------------------------- 1 | WIFI_STATUS_STA_STOP = 0 2 | WIFI_STATUS_STA_DISCONNECTED = 1 3 | WIFI_STATUS_STA_SHOULD_START = 2 4 | WIFI_STATUS_STA_STARTING = 3 5 | WIFI_STATUS_STA_START = 4 6 | WIFI_STATUS_STA_GOT_IP = 5 7 | WIFI_STATUS_STA_GOT_IP_SSID_PW_SAVED = 6 8 | WIFI_STATUS_STA_GOT_IP_SOMETHING_GOES_BAD = 7 9 | WIFI_STATUS_STA_GOT_IP_SUCCESS_SHOOTING = 8 10 | 11 | HOST = "172.20.10.2" 12 | PORT = 4567.to_s 13 | PATH = "/data" 14 | 15 | class Wifi 16 | def initialize 17 | start_wifi("hasumi-iPad", "b6kttbfqvee10") 18 | end 19 | 20 | def make_request(method, path, content) 21 | method + " " + path + " HTTP/1.1\r\n" + 22 | "Host: " + HOST + "\r\n" + 23 | "User-Agent: ESP32\r\n" + 24 | "accept: application/json\r\n" + 25 | "Content-Type: application/json\r\n" + 26 | "Content-Length: " + content.length.to_s + "\r\n" + 27 | "\r\n" + content 28 | end 29 | 30 | def post(data) 31 | if wifi_status >= WIFI_STATUS_STA_GOT_IP 32 | request = make_request('POST', PATH, '{"still.image_format":"jpeg"}') 33 | response = http_post(HOST, PORT, request) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/karg_test.rb: -------------------------------------------------------------------------------- 1 | class KargTest < PicoRubyTest 2 | desc "def method with karg" 3 | assert_equal(<<~RUBY, "1\n2") 4 | def m1(k: 1) 5 | puts k 6 | end 7 | m1 8 | m1(k:2) 9 | RUBY 10 | 11 | desc "required keyword" 12 | assert_equal(<<~RUBY, "true") 13 | def m2(k:) 14 | puts k 15 | end 16 | m2(k: true) 17 | RUBY 18 | 19 | desc "complicated case" 20 | assert_equal(<<~RUBY, "13") 21 | def m3(a, b, o1 = 1, o2 = 2, *c, k1:, k2: 3, k3:) 22 | puts k1 + k2 + k3 23 | end 24 | m3("dummy", "dummy", k3: 1, k2: 2, k1: 10) 25 | RUBY 26 | 27 | desc "block argument" 28 | assert_equal(<<~RUBY, "25") 29 | p = Proc.new do |a:, b: 11| 30 | a + b 31 | end 32 | puts p.call(b: 12, a: 13) 33 | RUBY 34 | 35 | desc "block argument 2" 36 | assert_equal(<<~RUBY, "{:a=>0}") 37 | def task(opt: {}) 38 | yield(opt) 39 | end 40 | task(opt: {a: 0}) do |opt| 41 | p opt 42 | end 43 | RUBY 44 | 45 | desc "karg and dict" 46 | assert_equal(<<~RUBY, "10") 47 | def combined_with_dict(k1: 0, k2:, **dict) 48 | k1 + k2 + dict[:k3] + dict[:k4] 49 | end 50 | p combined_with_dict(k2: 2, k1: 1, k3: 3, k4: 4) 51 | RUBY 52 | end 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | mruby-pico-compiler 2 | 3 | Copyright (c) HASUMI Hitoshi 2021 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | [ MIT license: http://www.opensource.org/licenses/mit-license.php ] 25 | 26 | -------------------------------------------------------------------------------- /include/banned.h: -------------------------------------------------------------------------------- 1 | #ifndef BANNED_H 2 | #define BANNED_H 3 | 4 | /* 5 | * This header lists functions that have been banned from our code base, 6 | * because they're too easy to misuse (and even if used correctly, 7 | * complicate audits). Including this header turns them into compile-time 8 | * errors. 9 | */ 10 | 11 | #define BANNED(func) sorry_##func##_is_a_banned_function 12 | 13 | #undef strcpy 14 | #define strcpy(x,y) BANNED(strcpy) 15 | #undef strcat 16 | #define strcat(x,y) BANNED(strcat) 17 | #undef strncpy 18 | #define strncpy(x,y,n) BANNED(strncpy) 19 | #undef strncat 20 | #define strncat(x,y,n) BANNED(strncat) 21 | 22 | #undef sprintf 23 | #undef vsprintf 24 | #ifdef HAVE_VARIADIC_MACROS 25 | #define sprintf(...) BANNED(sprintf) 26 | #define vsprintf(...) BANNED(vsprintf) 27 | #else 28 | #define sprintf(buf,fmt,arg) BANNED(sprintf) 29 | #define vsprintf(buf,fmt,arg) BANNED(vsprintf) 30 | #endif 31 | 32 | #undef gmtime 33 | #define gmtime(t) BANNED(gmtime) 34 | #undef localtime 35 | #define localtime(t) BANNED(localtime) 36 | #undef ctime 37 | #define ctime(t) BANNED(ctime) 38 | #undef ctime_r 39 | #define ctime_r(t, buf) BANNED(ctime_r) 40 | #undef asctime 41 | #define asctime(t) BANNED(asctime) 42 | #undef asctime_r 43 | #define asctime_r(t, buf) BANNED(asctime_r) 44 | 45 | #endif /* BANNED_H */ 46 | -------------------------------------------------------------------------------- /test/hash_test.rb: -------------------------------------------------------------------------------- 1 | class HashTest < PicoRubyTest 2 | 3 | desc "Generating a hash" 4 | assert_equal(<<~RUBY, '{:a=>1, "b"=>"2", :c=>true}') 5 | hash = {a: 1, "b" => "2", c: true} 6 | p hash 7 | RUBY 8 | 9 | desc "empty hash" 10 | assert_equal(<<~RUBY, "{}") 11 | p({}) 12 | RUBY 13 | 14 | desc "PICORUBY_HASH_SPLIT_COUNT" 15 | assert_equal(<<~RUBY, "0\n10\n25\n39\n79") 16 | hash = { 17 | k00: 0, k01: 1, k02: 2, k03: 3, k04: 4, k05: 5, k06: 6, k07: 7, k08: 8, k09: 9, 18 | k10: 10, k11: 11, k12: 12, k13: 13, k14: 14, k15: 15, k16: 16, k17: 17, k18: 18, k19: 19, 19 | k20: 20, k21: 21, k22: 22, k23: 23, k24: 24, k25: 25, k26: 26, k27: 27, k28: 28, k29: 29, 20 | k30: 30, k31: 31, k32: 32, k33: 33, k34: 34, k35: 35, k36: 36, k37: 37, k38: 38, k39: 39, 21 | k40: 40, k41: 41, k42: 42, k43: 43, k44: 44, k45: 45, k46: 46, k47: 47, k48: 48, k49: 49, 22 | k50: 50, k51: 51, k52: 52, k53: 53, k54: 54, k55: 55, k56: 56, k57: 57, k58: 58, k59: 59, 23 | k60: 60, k61: 61, k62: 62, k63: 63, k64: 64, k65: 65, k66: 66, k67: 67, k68: 68, k69: 69, 24 | k70: 70, k71: 71, k72: 72, k73: 73, k74: 74, k75: 75, k76: 76, k77: 77, k78: 78, k79: 79, 25 | } 26 | p hash[:k00] 27 | p hash[:k10] 28 | p hash[:k25] 29 | p hash[:k39] 30 | p hash[:k79] 31 | RUBY 32 | 33 | end 34 | -------------------------------------------------------------------------------- /test/if_test.rb: -------------------------------------------------------------------------------- 1 | class IfTest < PicoRubyTest 2 | desc "use if value 1" 3 | assert_equal(<<~RUBY, '1') 4 | res = if true 5 | 1 6 | else 7 | 0 8 | end 9 | p res 10 | RUBY 11 | 12 | desc "use if value 2" 13 | assert_equal(<<~RUBY, '0') 14 | res = if false 15 | 1 16 | else 17 | 0 18 | end 19 | p res 20 | RUBY 21 | 22 | desc "use if value 3" 23 | assert_equal(<<~RUBY, "0") 24 | ary = [0] 25 | res = if true 26 | ary[0] 27 | end 28 | puts res 29 | RUBY 30 | 31 | desc "use else value 1" 32 | assert_equal(<<~RUBY, "false") 33 | ary = [0] 34 | res = if !true 35 | ary[0] 36 | else 37 | ary[1] 38 | false 39 | end 40 | puts res 41 | RUBY 42 | 43 | desc "a complicated case" 44 | assert_equal(<<~RUBY, "hello\ntrue\n1\nnil") 45 | proc = Proc.new do puts "hello" end 46 | ary = [0, true, proc] 47 | res = if ary[1] 48 | ary[0] += 1 49 | ary[2].call 50 | ary[1] 51 | end 52 | puts res 53 | res = if false 54 | else 55 | puts ary[0] 56 | end 57 | p res 58 | RUBY 59 | 60 | desc "conditilnal operator 1" 61 | assert_equal(<<~RUBY, "true") 62 | a = [true, false] 63 | res = !nil ? a[0] : a[1] 64 | puts res 65 | RUBY 66 | 67 | desc "conditilnal operator 2" 68 | assert_equal(<<~RUBY, "false") 69 | a = [true, false] 70 | res = !true ? a[0] : a[1] 71 | puts res 72 | RUBY 73 | end 74 | 75 | -------------------------------------------------------------------------------- /include/mrb_state/microrb.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_MICRORB_H_ 2 | #define PICORBC_MICRORB_H_ 3 | 4 | #ifndef DISABLE_MRUBY 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /* from proc.h */ 13 | void mrb_env_unshare(mrb_state*, struct REnv*); 14 | 15 | /* from irep.h */ 16 | MRB_API mrb_value mrb_load_irep_cxt(mrb_state *mrb, const uint8_t *bin, picorbc_context *cxt); 17 | 18 | /* from dump.h */ 19 | MRB_API mrb_value mrb_load_irep_file_cxt(mrb_state*, FILE*, picorbc_context*); 20 | 21 | MRB_API mrb_value microrb_load_string(mrb_state *mrb, const char *str, picorbc_context *c); 22 | 23 | MRB_API mrb_value microrb_load_string_cxt(mrb_state *mrb, const char *s, picorbc_context *c); 24 | 25 | /* from proc.h */ 26 | struct REnv { 27 | MRB_OBJECT_HEADER; 28 | mrb_value *stack; 29 | struct mrb_context *cxt; 30 | mrb_sym mid; 31 | }; 32 | 33 | /* from proc.h */ 34 | static inline struct REnv * 35 | mrb_vm_ci_env(const mrb_callinfo *ci) 36 | { 37 | if (ci->u.env && ci->u.env->tt == MRB_TT_ENV) { 38 | return ci->u.env; 39 | } 40 | else { 41 | return NULL; 42 | } 43 | } 44 | 45 | /* from proc.h */ 46 | static inline void 47 | mrb_vm_ci_env_set(mrb_callinfo *ci, struct REnv *e) 48 | { 49 | if (ci->u.env) { 50 | if (ci->u.env->tt == MRB_TT_ENV) { 51 | if (e) { 52 | e->c = ci->u.env->c; 53 | ci->u.env = e; 54 | } 55 | else { 56 | ci->u.target_class = ci->u.env->c; 57 | } 58 | } 59 | else { 60 | if (e) { 61 | e->c = ci->u.target_class; 62 | ci->u.env = e; 63 | } 64 | } 65 | } 66 | else { 67 | ci->u.env = e; 68 | } 69 | } 70 | 71 | mrb_value microrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, picorbc_context *c); 72 | 73 | #endif /* DISABLE_MRUBY */ 74 | 75 | #endif /* PICORBC_MICRORB_H_*/ 76 | -------------------------------------------------------------------------------- /test/block_test.rb: -------------------------------------------------------------------------------- 1 | class BlockTest < PicoRubyTest 2 | desc "do block" 3 | assert_equal(<<~RUBY, "0\n1") 4 | [0, 1].each do |i| 5 | puts i 6 | end 7 | RUBY 8 | 9 | desc "do block chain" 10 | assert_equal(<<~RUBY, "1\n2") 11 | a = [0, 1] 12 | a.each do |i| 13 | a[i] += 1 14 | end.each do |i| 15 | puts i 16 | end 17 | RUBY 18 | 19 | desc "brace block" 20 | assert_equal(<<~RUBY, "0\n1") 21 | [0, 1].each { |i| puts i } 22 | RUBY 23 | 24 | desc "brace block chain" 25 | assert_equal(<<~RUBY, "1\n2") 26 | a = [0, 1] 27 | a.each { |i| a[i] += 1 }.each { |i| puts i } 28 | RUBY 29 | 30 | desc "Hash#each do |k, v|" 31 | assert_equal(<<~RUBY, "a\n0\nb\n1") 32 | h = {a: 0, b: 1} 33 | h.each do |k, v| 34 | puts k 35 | puts v 36 | end 37 | RUBY 38 | 39 | desc "each_with_index" 40 | assert_equal(<<~RUBY, "true\n0\nfalse\n1") 41 | a = [true, false] 42 | a.each_with_index do |val, index| 43 | puts val 44 | puts index 45 | end 46 | RUBY 47 | 48 | desc "block.call" 49 | assert_equal(<<~RUBY, "hello") 50 | def my_method(&block) 51 | block.call 52 | end 53 | my_method {puts "hello"} 54 | RUBY 55 | 56 | desc "yield" 57 | assert_equal(<<~RUBY, "hello") 58 | def my_method 59 | yield 60 | end 61 | my_method {puts "hello"} 62 | RUBY 63 | 64 | desc "lvar scope" 65 | assert_equal(<<~RUBY, "0") 66 | a = 1 67 | [0].each do |a| 68 | puts a 69 | end 70 | RUBY 71 | 72 | desc "return blk" 73 | assert_equal(<<~RUBY, "true") 74 | def a 75 | [1].each { return true } 76 | end 77 | p a 78 | RUBY 79 | 80 | desc "interpolation of lvar that is a block arg" 81 | assert_equal(<<~'RUBY', "2") 82 | [0].each { |i| 83 | puts "#{i + 2}" 84 | } 85 | RUBY 86 | 87 | end 88 | -------------------------------------------------------------------------------- /test/rescue_test.rb: -------------------------------------------------------------------------------- 1 | class RescueTest < PicoRubyTest 2 | 3 | desc "simple rescue" 4 | assert_equal(<<~RUBY, "true") 5 | begin 6 | raise 7 | rescue 8 | p true 9 | end 10 | RUBY 11 | 12 | desc "simple else" 13 | assert_equal(<<~RUBY, "false") 14 | begin 15 | 0 16 | rescue 17 | p true 18 | else 19 | p false 20 | end 21 | RUBY 22 | 23 | desc "simple ensure" 24 | assert_equal(<<~RUBY, "true\nnil") 25 | begin 26 | raise 27 | rescue 28 | p true 29 | else 30 | p false 31 | ensure 32 | p nil 33 | end 34 | RUBY 35 | 36 | desc "rescue error class" 37 | assert_equal(<<~RUBY, "RuntimeError\nMy Error") 38 | begin 39 | raise "My Error" 40 | rescue => e 41 | puts e.class 42 | puts e.message 43 | end 44 | RUBY 45 | 46 | desc "retry" 47 | assert_equal(<<~RUBY, "2") 48 | def my_method 49 | a = 0 50 | begin 51 | raise if a < 2 52 | rescue => e 53 | a += 1 54 | retry 55 | end 56 | p a 57 | end 58 | my_method 59 | RUBY 60 | 61 | if @@vm_select == :mruby 62 | 63 | desc "splat error class" 64 | assert_equal(<<~RUBY, "catched") 65 | errors = [TypeError] 66 | begin 67 | raise TypeError 68 | rescue *errors 69 | puts "catched" 70 | end 71 | RUBY 72 | 73 | desc "splat error class 2" 74 | assert_equal(<<~RUBY, "catched") 75 | errors = [TypeError] 76 | begin 77 | raise TypeError 78 | rescue StandardError, *errors 79 | puts "catched" 80 | end 81 | RUBY 82 | 83 | desc "rescue NoMethodError" 84 | assert_equal(<<~RUBY, "Yes") 85 | begin 86 | method_does_not_exist 87 | rescue ArgumentError => e 88 | puts "This should not happen 1" 89 | rescue NoMethodError => e 90 | puts "Yes" 91 | rescue => e 92 | puts "This should not happen 2" 93 | end 94 | RUBY 95 | 96 | end 97 | 98 | end 99 | -------------------------------------------------------------------------------- /src/node.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | const char *Node_valueName(Node *self) 9 | { 10 | return self->value.name; 11 | } 12 | 13 | void Node_setValue(Node *self, const char *s) 14 | { 15 | self->type = LITERAL; 16 | self->value.name = s; 17 | } 18 | 19 | bool Node_isAtom(Node *self) 20 | { 21 | if (self->type == ATOM) return true; 22 | return false; 23 | } 24 | 25 | bool Node_isCons(Node *self) 26 | { 27 | if (self->type == CONS) return true; 28 | return false; 29 | } 30 | 31 | bool Node_isLiteral(Node *self) 32 | { 33 | return (self->type == LITERAL); 34 | } 35 | 36 | AtomType Node_atomType(Node *self) 37 | { 38 | if (self == NULL || self->cons.car == NULL || !Node_isAtom(self->cons.car)) { 39 | return ATOM_NONE; 40 | } else { 41 | if (self->cons.car->type != ATOM) { 42 | return ATOM_NONE; 43 | } else { 44 | return self->cons.car->atom.type; 45 | } 46 | } 47 | } 48 | 49 | const char *Node_literalName(Node *self) 50 | { 51 | if (self->cons.car == NULL || !Node_isLiteral(self->cons.car)) { 52 | return NULL; 53 | } else { 54 | return Node_valueName(self->cons.car); 55 | } 56 | } 57 | 58 | NodeBox *Node_newBox(ParserState *p) 59 | { 60 | int size = sizeof(NodeBox) + sizeof(Node) * p->node_box_size; 61 | NodeBox *node_box = (NodeBox *)picorbc_alloc(size); 62 | memset(node_box, 0, size); 63 | if (p->current_node_box) p->current_node_box->next = node_box; 64 | p->current_node_box = node_box; 65 | node_box->next = NULL; 66 | node_box->size = p->node_box_size; 67 | node_box->index = 0; 68 | return node_box; 69 | } 70 | 71 | Node *Node_new(ParserState *p) 72 | { 73 | NodeBox *box = p->current_node_box; 74 | box->index++; 75 | if (box->index == box->size) { 76 | box = Node_newBox(p); 77 | } 78 | return (Node *)((Node *)(&box->nodes) + box->index); 79 | } 80 | 81 | void Node_freeAllNode(NodeBox *box) 82 | { 83 | NodeBox *next; 84 | while (box) { 85 | next = box->next; 86 | picorbc_free(box); 87 | box = next; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /include/token.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_TOKEN_H_ 2 | #define PICORBC_TOKEN_H_ 3 | 4 | #include 5 | #include 6 | 7 | typedef enum state 8 | { 9 | EXPR_NONE = 0b00000000000000, 10 | EXPR_BEG = 0b00000000000001, /* beginning of an expr. right after \n ( { [ ! ? : , op= etc. ignore newline, +/- is a sign. */ 11 | EXPR_VALUE = 0b00000000000010, 12 | EXPR_END = 0b00000000000100, /* able to be terminal of a statement --eg: after literal or )-- except when EXPR_ENDARG. newline significant, +/- is a operator. */ 13 | EXPR_ENDARG = 0b00000000001000, /* special case of EXPR_END. right after RPAREN which corresponds to tLPAREN_ARG. newline significant, +/- is a operator. */ 14 | EXPR_ENDFN = 0b00000000010000, 15 | EXPR_END_ANY = 0b00000000011100, 16 | EXPR_ARG = 0b00000000100000, /* possibly right after method name of right after [ excpept when EXPR_CMDARG. newline significant, +/- is a operator. */ 17 | EXPR_CMDARG = 0b00000001000000, /* expecting the first argument of a normal method. newline significant, +/- is a operator. */ 18 | EXPR_ARG_ANY = 0b00000001100000, 19 | EXPR_MID = 0b00000010000000, /* right after return break rescue. binary op like * & will be invalid. newline significant, +/- is a operator. */ 20 | EXPR_FNAME = 0b00000100000000, /* right after def, alias, undef, : of a symbol. ` becomes a name. ignore newline, no reserved words. */ 21 | EXPR_DOT = 0b00001000000000, /* right after `.', no reserved words. */ 22 | EXPR_COLON2 = 0b100000000000000, /* right after `::', no reserved words. */ 23 | EXPR_CLASS = 0b00010000000000, /* immediate after `class', no here document. */ 24 | EXPR_BEG_ANY = 0b00010010000010, 25 | EXPR_LABEL = 0b00100000000000, 26 | EXPR_LABELED = 0b01000000000000, 27 | EXPR_FITEM = 0b10000000000000 28 | } State; 29 | 30 | typedef uint8_t Type; //defined in parse.h 31 | 32 | typedef struct token 33 | { 34 | int line_num; 35 | int pos; 36 | Type type; 37 | State state; 38 | struct token *prev; 39 | struct token *next; 40 | char *value; 41 | int refCount; 42 | } Token; 43 | 44 | Token *Token_new(void); 45 | 46 | void Token_free(Token *self); 47 | 48 | void Token_GC(Token *currentToken); 49 | 50 | bool Token_exists(Token* const self); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /test/masgn_test.rb: -------------------------------------------------------------------------------- 1 | class MasgnTest < PicoRubyTest 2 | desc "basic case 1" 3 | assert_equal(<<~RUBY, "1\n2") 4 | a, b = 1, 2 5 | p a, b 6 | RUBY 7 | 8 | desc "basic case 2" 9 | assert_equal(<<~RUBY, "1\n2") 10 | a, b = [1, 2] 11 | p a, b 12 | RUBY 13 | 14 | desc "RHS has only one item" 15 | assert_equal(<<~RUBY, "1\nnil") 16 | a, b = 1 17 | p a, b 18 | RUBY 19 | 20 | desc "baz should be nil" 21 | assert_equal(<<~RUBY, "1\n2\nnil") 22 | baz = true 23 | foo, bar, baz = 1, 2 24 | p foo, bar, baz 25 | RUBY 26 | 27 | desc "3 should be ignored" 28 | assert_equal(<<~RUBY, "1\n2") 29 | foo, bar = 1, 2, 3 30 | p foo, bar 31 | RUBY 32 | 33 | desc "single pre" 34 | assert_equal(<<~RUBY, "[1, 2, 3]") 35 | foo = 1, 2, 3 36 | p foo 37 | RUBY 38 | 39 | desc "single rest" 40 | assert_equal(<<~RUBY, "[1, 2, 3]") 41 | *foo = 1, 2, 3 42 | p foo 43 | RUBY 44 | 45 | desc "rest gets the rest" 46 | assert_equal(<<~RUBY, "1\n[2, 3]") 47 | foo,*bar = 1, 2, 3 48 | p foo, bar 49 | RUBY 50 | 51 | desc "post should be nil" 52 | assert_equal(<<~RUBY, "nil") 53 | post = true 54 | pre1, pre2, *rest, post = 1, 2 55 | p post 56 | RUBY 57 | 58 | # TODO 59 | #desc "post should be nil" 60 | #assert_equal(<<~RUBY, "1\n2\n3") 61 | # (foo, bar), baz = [1, 2], 3 62 | # p foo, bar, baz 63 | #RUBY 64 | 65 | desc "complecated case" 66 | assert_equal(<<~'RUBY', "1\n[\"a\", \"b\", 2]") 67 | class C 68 | attr_accessor :foo, :bar 69 | def foo=( v ) 70 | @foo = v 71 | end 72 | def []=(i,v) 73 | @bar = ["a", "b", "c"] 74 | @bar[i] = v 75 | end 76 | end 77 | obj = C.new 78 | obj.foo, obj[2] = 1, 2 79 | p obj.foo, obj.bar 80 | RUBY 81 | 82 | if @@vm_select == :mruby 83 | desc "RHS has only one item, LHS has a rest and a post" 84 | assert_equal(<<~RUBY, "1\nnil\n[]\nnil") 85 | a, b, *c, d = 1 86 | p a, b, c, d 87 | RUBY 88 | 89 | desc "RHS has only one item which is an array, LHS has a rest and a post" 90 | assert_equal(<<~RUBY, "0\n[]\n1") 91 | ary = [0, 1] 92 | a, *b, c = ary 93 | p a, b, c 94 | RUBY 95 | 96 | desc "splat in RHS" 97 | assert_equal(<<~RUBY, "1\n[9, 8]\n3\n4") 98 | ary = [9, 8] 99 | a,*b,c,d=1,*ary,3,4 100 | p a,b,c,d 101 | RUBY 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /test/def_test.rb: -------------------------------------------------------------------------------- 1 | class DefTest < PicoRubyTest 2 | desc "def method without arg" 3 | assert_equal(<<~RUBY, "My method!") 4 | def my_method 5 | "My method!" 6 | end 7 | puts my_method 8 | RUBY 9 | 10 | desc "def method with a mandatory arg" 11 | assert_equal(<<~RUBY, "My arg") 12 | def my_method(arg) 13 | arg 14 | end 15 | puts my_method("My arg") 16 | RUBY 17 | 18 | desc "endless def" 19 | assert_equal(<<~RUBY, "0") 20 | def my_method(arg) = arg.to_i 21 | puts my_method(nil) 22 | RUBY 23 | 24 | desc "def method with an optional arg" 25 | assert_equal(<<~RUBY, "default") 26 | def my_method(arg = "default") 27 | arg 28 | end 29 | puts my_method 30 | RUBY 31 | 32 | desc "def method with three optional args" 33 | assert_equal(<<~RUBY, "\"default\"\nnil\n0") 34 | def my_method(arg1 = "default", arg2=nil, arg3=0) 35 | p arg1, arg2, arg3 36 | end 37 | my_method 38 | RUBY 39 | 40 | desc "def method with a mandatory arg and an optional arg" 41 | assert_equal(<<~RUBY, "hey\nyou") 42 | def my_method(marg, opt = "optional") 43 | puts marg, opt 44 | end 45 | my_method("hey", "you") 46 | RUBY 47 | 48 | desc "block" 49 | assert_equal(<<~RUBY, "1") 50 | def m(&block) 51 | block.call 52 | end 53 | m { puts 1 } 54 | RUBY 55 | 56 | desc "marg, optarg and block" 57 | assert_equal(<<~RUBY, "1") 58 | def m(a,b,&block) 59 | block.call 60 | end 61 | m(8,9) { puts 1 } 62 | RUBY 63 | 64 | desc "marg, optarg and block / part.2" 65 | assert_equal(<<~RUBY, "8\n9") 66 | def m(a,b,&block) 67 | block.call(a,b) 68 | end 69 | m(8,9) { |x, y| puts x, y } 70 | RUBY 71 | 72 | desc "def !" 73 | assert_equal(<<~RUBY, ":!") 74 | p(def !() end) 75 | RUBY 76 | 77 | desc "def !=" 78 | assert_equal(<<~RUBY, ":!=") 79 | p(def !=() end) 80 | RUBY 81 | 82 | desc "def `" 83 | assert_equal(<<~RUBY, ":`") 84 | p(def `() end) 85 | RUBY 86 | 87 | desc "def ==" 88 | assert_equal(<<~RUBY, ":==") 89 | p(def ==() end) 90 | RUBY 91 | 92 | desc "def ===" 93 | assert_equal(<<~RUBY, ":===") 94 | p(def ===() end) 95 | RUBY 96 | 97 | desc "def <=>" 98 | assert_equal(<<~RUBY, ":<=>") 99 | p(def <=>() end) 100 | RUBY 101 | 102 | desc "def <<" 103 | assert_equal(<<~RUBY, ":<<") 104 | p(def <<() end) 105 | RUBY 106 | 107 | desc "def >>" 108 | assert_equal(<<~RUBY, ":>>") 109 | p(def >>() end) 110 | RUBY 111 | 112 | desc "def <" 113 | assert_equal(<<~RUBY, ":<") 114 | p(def <() end) 115 | RUBY 116 | 117 | desc "def >" 118 | assert_equal(<<~RUBY, ":>") 119 | p(def >() end) 120 | RUBY 121 | end 122 | -------------------------------------------------------------------------------- /src/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | fmemstream *fmemstreamopen(const char *mem) 10 | { 11 | fmemstream *stream = picorbc_alloc(sizeof(fmemstream)); 12 | stream->pos = 0; 13 | stream->mem = mem; 14 | stream->size = strlen(mem); 15 | return stream; 16 | } 17 | 18 | /* 19 | * the same interface as feof() 20 | */ 21 | int fmemeof(FILE *file) 22 | { 23 | fmemstream *stream = (fmemstream *)file; 24 | if (stream->pos < stream->size) { 25 | return 0; 26 | } else if (stream->pos == stream->size) { 27 | return 1; 28 | } else { 29 | FATALP("fmemeof error"); 30 | } 31 | return -1; 32 | } 33 | 34 | /* 35 | * the same interface as fgets() 36 | */ 37 | char *fmemgets(char *s, int max, FILE *file) 38 | { 39 | if (fmemeof(file) != 0) return NULL; 40 | fmemstream *stream = (fmemstream *)file; 41 | char *end = strchr(&stream->mem[stream->pos], (int)'\n'); 42 | int len; 43 | if (end == NULL) { 44 | if ( (stream->size - stream->pos) < max ) { 45 | len = stream->size - stream->pos; 46 | } else { 47 | len = max - 1; 48 | } 49 | } else { 50 | len = 1 + (intptr_t)end - (intptr_t)&stream->mem[stream->pos]; 51 | } 52 | if (len < max) { 53 | memcpy(s, &stream->mem[stream->pos], len); 54 | s[len] = '\0'; 55 | stream->pos += len; 56 | } else { 57 | memcpy(s, &stream->mem[stream->pos], max - 1); 58 | s[max] = '\0'; 59 | stream->pos += max - 1; 60 | } 61 | return s; 62 | } 63 | 64 | StreamInterface *StreamInterface_new(FILE *fp, const char *c, StreamType type) 65 | { 66 | StreamInterface *si = picorbc_alloc(sizeof(StreamInterface)); 67 | si->type = type; 68 | uint16_t length; 69 | switch (si->type) { 70 | case STREAM_TYPE_FILE: 71 | if (fp) { 72 | si->stream = (void *)fp; 73 | } else if( (si->stream = (void *)fopen(c, "r" ) ) == NULL ) { 74 | FATALP("picorbc: cannot open program file. (%s)", c); 75 | picorbc_free(si); 76 | return NULL; 77 | } 78 | si->fgetsProc = fgets; 79 | si->feofProc = feof; 80 | si->node_box_size = 255; 81 | break; 82 | case STREAM_TYPE_MEMORY: 83 | si->stream = (void *)fmemstreamopen(c); 84 | si->fgetsProc = fmemgets; 85 | si->feofProc = fmemeof; 86 | length = strlen(c); 87 | if (length < 20) { 88 | si->node_box_size = 20; 89 | } else if (length < 100) { 90 | si->node_box_size = 50; 91 | } else if (length < 200) { 92 | si->node_box_size = 100; 93 | } else { 94 | si->node_box_size = 255; 95 | } 96 | break; 97 | default: 98 | FATALP("error at Stream_new()"); 99 | } 100 | return si; 101 | } 102 | 103 | void StreamInterface_free(StreamInterface *si) 104 | { 105 | switch (si->type) { 106 | case STREAM_TYPE_FILE: 107 | //fclose() should be invoked where fopen was called 108 | picorbc_free(si); 109 | break; 110 | case STREAM_TYPE_MEMORY: 111 | picorbc_free(si->stream); 112 | picorbc_free(si); 113 | break; 114 | default: 115 | FATALP("error at Stream_free()"); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifdef PICORUBY_DEBUG 7 | /* GLOBAL */ 8 | int alloc_count = 0; 9 | int free_count = 0; 10 | AllocList *last_alloc; 11 | 12 | void print_memory(void) 13 | { 14 | #ifndef MRBC_ALLOC_LIBC 15 | struct MRBC_ALLOC_STATISTICS *stats; 16 | mrbc_alloc_statistics(stats); 17 | DEBUGP("Memory total:%d, used:%d, free:%d, fragment:%d", 18 | stats->total, stats->used, stats->free, stats->fragmentation ); 19 | #endif 20 | } 21 | void memcheck(void) 22 | { 23 | DEBUGP("---MEMCHECK---\nalloc_count: %d, free_count: %d", alloc_count, free_count); 24 | AllocList *ah = last_alloc; 25 | while (ah != NULL) { 26 | WARNP("MemoryLeakDetected! alloc_count: %d, ptr: %p", ah->count, ah->ptr); 27 | if (ah->prev != NULL) { 28 | ah = ah->prev; 29 | free(ah->next); 30 | } else { 31 | free(ah); 32 | break; 33 | } 34 | } 35 | } 36 | #endif /* PICORUBY_DEBUG */ 37 | 38 | void *picorbc_alloc(size_t size) 39 | { 40 | void *ptr; 41 | ptr = PICORBC_ALLOC(size); 42 | #ifdef PICORUBY_DEBUG 43 | DEBUGP("alloc_count: %d, ptr: %p, size: %d", alloc_count, ptr, (int)size); 44 | print_memory(); 45 | alloc_count++; 46 | // if (alloc_count == 2){ 47 | // DEBUGP("sssss"); 48 | // } 49 | AllocList *ah = malloc(sizeof(AllocList)); 50 | ah->count = alloc_count; 51 | ah->ptr = ptr; 52 | if (last_alloc != NULL) { 53 | ah->prev = last_alloc; 54 | last_alloc->next = ah; 55 | } else { 56 | ah->prev = NULL; 57 | } 58 | ah->next = NULL; 59 | last_alloc = ah; 60 | #endif 61 | return ptr; 62 | } 63 | 64 | void picorbc_free(void *ptr) 65 | { 66 | if (ptr == NULL) return; 67 | DEBUGP("free: %p", ptr); 68 | PICORBC_FREE(ptr); 69 | #ifdef PICORUBY_DEBUG 70 | print_memory(); 71 | free_count++; 72 | AllocList *ah = last_alloc; 73 | if (ah->ptr == ptr) { 74 | last_alloc = ah->prev; 75 | free(ah); 76 | } else { 77 | while (ah != NULL) { 78 | if (ah->ptr == ptr) { 79 | if (ah->prev != NULL) { 80 | ah->prev->next = ah->next; 81 | } 82 | if (ah->next != NULL) { 83 | ah->next->prev = ah->prev; 84 | } 85 | free(ah); 86 | break; 87 | } 88 | ah = ah->prev; 89 | } 90 | } 91 | #endif 92 | } 93 | 94 | char *strsafencpy(char *s1, const char *s2, size_t n, size_t max) 95 | { 96 | DEBUGP("s1: `%s`, s2: `%s`, n: %d, max: %d", s1, s2, (int)n, (int)max); 97 | if (n < max) { 98 | memcpy(s1, s2, n + 1); 99 | DEBUGP("Copied: %s", s1); 100 | } else { 101 | FATALP("Can't cpy string!"); 102 | } 103 | return s1; 104 | } 105 | 106 | char *strsafecpy(char *s1, const char *s2, size_t max) 107 | { 108 | DEBUGP("s1: `%s`, s2: `%s`, max: %d", s1, s2, (int)max); 109 | return strsafencpy(s1, s2, strlen(s2), max); 110 | } 111 | 112 | char *strsafecat(char *dst, const char *src, size_t max) 113 | { 114 | size_t lensrc = strlen(src); 115 | size_t lendst = strlen(dst); 116 | if (lendst + lensrc < max) { 117 | memcpy(dst+lendst, src, lensrc+1); 118 | } else { 119 | FATALP("Can't cat string!"); 120 | } 121 | return dst; 122 | } 123 | -------------------------------------------------------------------------------- /src/my_regex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | typedef struct preg_cache { 10 | const char *pattern; 11 | regex_t preg; 12 | } PregCache; 13 | 14 | #define PREG_CACHE_SIZE 23 15 | 16 | PregCache *global_preg_cache; 17 | 18 | void MyRegex_setup(bool use_global_preg_cache) 19 | { 20 | #ifndef MRBC_ALLOC_LIBC 21 | RegexSetAllocProcs(picorbc_alloc, picorbc_free); 22 | #endif 23 | if (use_global_preg_cache) { 24 | global_preg_cache = picorbc_alloc(sizeof(PregCache) * PREG_CACHE_SIZE); 25 | memset(global_preg_cache, 0, sizeof(PregCache) * PREG_CACHE_SIZE); 26 | } else { 27 | global_preg_cache = NULL; 28 | } 29 | } 30 | 31 | void regex_cache_free(void) 32 | { 33 | int i = 0; 34 | PregCache *cache = global_preg_cache; 35 | while (cache->preg.atoms && i < PREG_CACHE_SIZE) { 36 | regfree(&cache->preg); 37 | cache++; 38 | i++; 39 | } 40 | } 41 | 42 | void MyRegexCache_free(void) 43 | { 44 | if (global_preg_cache == NULL) return; 45 | regex_cache_free(); 46 | picorbc_free(global_preg_cache); 47 | } 48 | 49 | bool regex_match(char *str, const char *pattern, bool resultRequired, RegexResult *result) 50 | { 51 | int i = 0; 52 | int size; 53 | bool status = false; 54 | PregCache *cache; 55 | 56 | if (global_preg_cache) { 57 | cache = global_preg_cache; 58 | while (cache->preg.atoms && i < PREG_CACHE_SIZE) { 59 | if (cache->pattern == pattern) break; 60 | cache++; 61 | i++; 62 | } 63 | if (i == PREG_CACHE_SIZE) { 64 | regex_cache_free(); 65 | memset(global_preg_cache, 0, sizeof(PregCache) * PREG_CACHE_SIZE); 66 | cache = global_preg_cache; 67 | } 68 | } else { 69 | cache = picorbc_alloc(sizeof(PregCache)); 70 | cache->preg.atoms = NULL; 71 | } 72 | 73 | if (cache->preg.atoms == NULL) { 74 | cache->pattern = pattern; 75 | if (regcomp(&cache->preg, pattern, REG_EXTENDED|REG_NEWLINE) != 0){ 76 | FATALP("regcomp failed: /%s/", pattern); 77 | } 78 | } 79 | 80 | size = (&cache->preg)->re_nsub + 1; 81 | regmatch_t pmatch[size]; // number of subexpression + 1 82 | 83 | if (regexec(&cache->preg, str, size, pmatch, 0) != 0){ 84 | DEBUGP("no match: %s", pattern); 85 | } else { 86 | DEBUGP("match!: %s", pattern); 87 | status = true; 88 | } 89 | 90 | if (status && resultRequired) { 91 | for (i = 0; i < size; i++){ 92 | int startIndex = pmatch[i].rm_so; 93 | int endIndex = pmatch[i].rm_eo; 94 | if (startIndex == -1 || endIndex == -1) { 95 | continue; 96 | } 97 | DEBUGP("match[%d] index [start, end] = %d, %d", i, startIndex, endIndex); 98 | strsafencpy((result + i)->value, str + startIndex, endIndex - startIndex, MAX_TOKEN_LENGTH); 99 | (result + i)->value[endIndex - startIndex] = '\0'; 100 | DEBUGP("match result: %s", (result + i)->value); 101 | } 102 | } 103 | 104 | if (global_preg_cache == NULL) { 105 | regfree(&cache->preg); 106 | picorbc_free(cache); 107 | } 108 | 109 | return status; 110 | } 111 | 112 | bool Regex_match2(char *str, const char *pattern) 113 | { 114 | return regex_match(str, pattern, false, NULL); 115 | } 116 | 117 | bool Regex_match3(char *str, const char *pattern, RegexResult result[REGEX_MAX_RESULT_NUM]) 118 | { 119 | return regex_match(str, pattern, true, result); 120 | } 121 | -------------------------------------------------------------------------------- /src/microrb.c: -------------------------------------------------------------------------------- 1 | #ifndef DISABLE_MRUBY 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | MRB_API mrb_value 9 | microrb_load_file_cxt(mrb_state *mrb, FILE *fp, picorbc_context *cxt) 10 | { 11 | mrb_value ret; 12 | StreamInterface *si = StreamInterface_new(fp, NULL, STREAM_TYPE_FILE); 13 | ParserState *p = Compiler_parseInitState(0, si->node_box_size); 14 | if (Compiler_compile(p, si, cxt)) { 15 | mrb_load_irep_cxt(mrb, p->scope->vm_code, cxt); 16 | } else { 17 | // TODO 18 | } 19 | StreamInterface_free(si); 20 | Compiler_parserStateFree(p); 21 | return ret; 22 | } 23 | 24 | /* binary header */ 25 | #define RITE_BINARY_IDENT "RITE" 26 | struct rite_binary_header { 27 | uint8_t binary_ident[4]; /* Binary Identifier */ 28 | uint8_t major_version[2]; /* Binary Format Major Version */ 29 | uint8_t minor_version[2]; /* Binary Format Minor Version */ 30 | uint8_t binary_size[4]; /* Binary Size */ 31 | uint8_t compiler_name[4]; /* Compiler name */ 32 | uint8_t compiler_version[4]; 33 | }; 34 | 35 | #define DETECT_SIZE 64 36 | 37 | static inline uint32_t 38 | bin_to_uint32(const uint8_t *bin) 39 | { 40 | return (uint32_t)bin[0] << 24 | 41 | (uint32_t)bin[1] << 16 | 42 | (uint32_t)bin[2] << 8 | 43 | (uint32_t)bin[3]; 44 | } 45 | 46 | mrb_value 47 | microrb_load_detect_file_cxt(mrb_state *mrb, FILE *fp, picorbc_context *cxt) 48 | { 49 | union { 50 | char b[DETECT_SIZE]; 51 | struct rite_binary_header h; 52 | } leading; 53 | size_t bufsize; 54 | 55 | if (fp == NULL) { 56 | return mrb_nil_value(); 57 | } 58 | 59 | bufsize = fread(leading.b, sizeof(char), sizeof(leading), fp); 60 | if (bufsize < sizeof(leading.h) || 61 | memcmp(leading.h.binary_ident, RITE_BINARY_IDENT, sizeof(leading.h.binary_ident)) != 0 || 62 | memchr(leading.b, '\0', bufsize) == NULL) { 63 | //return mrb_load_exec(mrb, mrb_parse_file_continue(mrb, fp, leading.b, bufsize, cxt), cxt); 64 | rewind(fp); 65 | return microrb_load_file_cxt(mrb, fp, cxt); 66 | } 67 | else { 68 | // size_t binsize; 69 | // uint8_t *bin; 70 | // mrb_value bin_obj = mrb_nil_value(); /* temporary string object */ 71 | // mrb_value result; 72 | // 73 | // binsize = bin_to_uint32(leading.h.binary_size); 74 | // bin_obj = mrb_str_new(mrb, NULL, binsize); 75 | // bin = (uint8_t *)RSTRING_PTR(bin_obj); 76 | // memcpy(bin, leading.b, bufsize); 77 | // if (binsize > bufsize && 78 | // fread(bin + bufsize, binsize - bufsize, 1, fp) == 0) { 79 | // binsize = bufsize; 80 | // /* The error is reported by mrb_load_irep_buf_cxt() */ 81 | // } 82 | // 83 | // result = mrb_load_irep_buf_cxt(mrb, bin, binsize, cxt); 84 | // if (mrb_string_p(bin_obj)) mrb_str_resize(mrb, bin_obj, 0); 85 | // return result; 86 | rewind(fp); 87 | return mrb_load_irep_file_cxt(mrb, fp, cxt); 88 | } 89 | } 90 | 91 | MRB_API mrb_value 92 | microrb_load_string_cxt(mrb_state *mrb, const char *str, picorbc_context *cxt) 93 | { 94 | mrb_value ret; 95 | StreamInterface *si = StreamInterface_new(NULL, (char *)str, STREAM_TYPE_MEMORY); 96 | ParserState *p = Compiler_parseInitState(0, si->node_box_size); 97 | if (Compiler_compile(p, si, cxt)) { 98 | mrb_load_irep_cxt(mrb, p->scope->vm_code, cxt); 99 | } else { 100 | // TODO 101 | } 102 | StreamInterface_free(si); 103 | Compiler_parserStateFree(p); 104 | return ret; 105 | } 106 | 107 | #endif /* DISABLE_MRUBY */ 108 | -------------------------------------------------------------------------------- /test/helper/test.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby 2 | 3 | require 'fileutils' 4 | require 'rake/file_list' 5 | require "tempfile" 6 | 7 | class PicoRubyTest 8 | def initialize 9 | @@pending = false 10 | @@exit_code = 0 11 | @@success_count = 0 12 | @@pending_count = 0 13 | @@brown = "\e[33m" 14 | @@green = "\e[32;1m" 15 | @@red = "\e[31;1m" 16 | @@reset = "\e[m" 17 | @@failure_struct = Struct.new(:filename, :description, :script, :expected, :actual) 18 | @@failures = [] 19 | @@description = "" 20 | @@mruby_path = ENV['MRUBY_COMMAND'] 21 | @@picorbc_path = ENV['PICORBC_COMMAND'] 22 | @@vm_select = ENV['USE_MRUBY'] ? :mruby : :mrubyc 23 | puts 24 | puts <<~"PREFACE" 25 | Virtual machine: #{@@vm_select} 26 | mruby_path: #{@@mruby_path} 27 | picorbc_path: #{@@picorbc_path} 28 | PREFACE 29 | puts 30 | end 31 | 32 | def exit_code 33 | @@exit_code 34 | end 35 | 36 | def test 37 | puts 'Start test' 38 | file = "../../#{ENV['FILE'] || '*_test.rb'}" 39 | Dir.glob(File.expand_path(file, __FILE__)).each do |file| 40 | @@filename = File.basename(file) 41 | load file 42 | @@pending = false 43 | end 44 | puts 45 | puts "Summary:" 46 | print @@green 47 | puts " Success: #{@@success_count}" 48 | print @@brown if @@pending_count > 0 49 | puts " Pending: #{@@pending_count}#{@@reset}" 50 | if @@failures.count > 0 51 | print @@red 52 | else 53 | print @@green 54 | end 55 | puts " Failure: #{@@failures.count}" 56 | @@failures.each do |failure| 57 | puts " File: #{failure.filename}" 58 | puts " Description: #{failure.description}" 59 | puts " Script:" 60 | puts " #{failure.script.gsub(/\n/, %Q/\n /)}" 61 | puts " Expected:" 62 | puts " #{failure.expected.gsub(/\n/, %Q/\n /)}" 63 | puts " Actual:" 64 | puts " #{failure.actual.gsub(/\n/, %Q/\n /)}" 65 | end 66 | print @@reset 67 | end 68 | 69 | def self.desc(text) 70 | @@description = text 71 | end 72 | 73 | def self.assert_equal(script, expected) 74 | if @@pending 75 | print "#{@@brown}.#{@@reset}" 76 | @@pending_count += 1 77 | return 78 | end 79 | GC.disable # To keep temporary files 80 | if @@vm_select == :mruby 81 | rbfile = String.new 82 | mrbfile = String.new 83 | actual = nil 84 | temp_rbfile = Tempfile.open do |f| 85 | f.puts script 86 | f 87 | end 88 | rbfile = temp_rbfile.path 89 | temp_mrbfile = Tempfile.open 90 | mrbfile = temp_mrbfile.path 91 | # For GitHub Actions 92 | FileUtils.chmod 0755, rbfile 93 | FileUtils.chmod 0755, mrbfile 94 | `#{@@picorbc_path} #{rbfile} -o #{mrbfile}` 95 | actual = `#{@@mruby_path} -b '#{mrbfile}'`.chomp.gsub(/\r/, "") 96 | else 97 | actual = `#{@@mruby_path} -e '#{script}'`.chomp.gsub(/\r/, "") 98 | end 99 | GC.enable 100 | if actual == expected 101 | print "#{@@green}." 102 | @@success_count += 1 103 | else 104 | print "#{@@red}." 105 | @@failures << @@failure_struct.new(@@filename, @@description, script.chomp, expected, actual) 106 | @@exit_code = 1 107 | end 108 | print @@reset 109 | end 110 | 111 | def self.pending 112 | @@pending = true 113 | end 114 | end 115 | 116 | picoruby_test = PicoRubyTest.new 117 | picoruby_test.test 118 | 119 | exit picoruby_test.exit_code 120 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_DEBUG_H_ 2 | #define PICORBC_DEBUG_H_ 3 | 4 | #include 5 | #include 6 | 7 | #define LOGLEVEL_FATAL 0 8 | #define LOGLEVEL_ERROR 1 9 | #define LOGLEVEL_WARN 2 10 | #define LOGLEVEL_INFO 3 11 | #define LOGLEVEL_DEBUG 4 12 | 13 | /* GLOBAL */ 14 | extern int loglevel; 15 | 16 | #ifdef PICORUBY_DEBUG 17 | 18 | #define DEBUGP(fmt, ...) \ 19 | do { \ 20 | if (loglevel >= LOGLEVEL_DEBUG) { \ 21 | printf("\033[34;1m[DEBUG] "); \ 22 | DEBUG_PRINTF(fmt, ##__VA_ARGS__); \ 23 | printf("\033[m\n"); \ 24 | } \ 25 | } while (0) 26 | #define INFOP(fmt, ...) \ 27 | do { \ 28 | if (loglevel >= LOGLEVEL_INFO) { \ 29 | printf("[INFO] "); \ 30 | DEBUG_PRINTF(fmt, ##__VA_ARGS__); \ 31 | printf("\033[m\n"); \ 32 | } \ 33 | } while (0) 34 | #define WARNP(fmt, ...) \ 35 | do { \ 36 | if (loglevel >= LOGLEVEL_WARN) { \ 37 | printf("\033[35;1m[WARN] "); \ 38 | DEBUG_PRINTF(fmt, ##__VA_ARGS__); \ 39 | printf("\033[m\n"); \ 40 | } \ 41 | } while (0) 42 | #define ERRORP(fmt, ...) \ 43 | do { \ 44 | if (loglevel >= LOGLEVEL_ERROR) { \ 45 | printf("\033[31;1m[ERROR] "); \ 46 | DEBUG_PRINTF(fmt, ##__VA_ARGS__); \ 47 | printf("\033[m\n"); \ 48 | } \ 49 | } while (0) 50 | #define FATALP(fmt, ...) \ 51 | do { \ 52 | if (loglevel >= LOGLEVEL_FATAL) { \ 53 | printf("\033[37;41;1m[FATAL] "); \ 54 | DEBUG_PRINTF(fmt, ##__VA_ARGS__); \ 55 | printf("\033[m\n"); \ 56 | } \ 57 | } while (0) 58 | #define DEBUG_PRINTF(fmt, ...) \ 59 | do { \ 60 | printf("%s:%d %s() ", \ 61 | __FILE__, __LINE__, __func__); \ 62 | printf(fmt, ##__VA_ARGS__); \ 63 | } while (0) 64 | #else 65 | #define DEBUGP(fmt, ...) /* omit */ 66 | #define INFOP(fmt, ...) /* omit */ 67 | #define WARNP(fmt, ...) \ 68 | do { \ 69 | if (loglevel >= LOGLEVEL_WARN) { \ 70 | fprintf(stdout, "[WARN] "); \ 71 | fprintf(stdout, fmt, ##__VA_ARGS__); \ 72 | fprintf(stdout, "\n"); \ 73 | } \ 74 | } while (0) 75 | #define ERRORP(fmt, ...) \ 76 | do { \ 77 | if (loglevel >= LOGLEVEL_ERROR) { \ 78 | fprintf(stderr, "[ERROR] "); \ 79 | fprintf(stderr, fmt, ##__VA_ARGS__); \ 80 | fprintf(stderr, "\n"); \ 81 | } \ 82 | } while (0) 83 | #define FATALP(fmt, ...) \ 84 | do { \ 85 | if (loglevel >= LOGLEVEL_FATAL) { \ 86 | fprintf(stderr, "[FATAL] "); \ 87 | fprintf(stderr, fmt, ##__VA_ARGS__); \ 88 | fprintf(stderr, "\n"); \ 89 | } \ 90 | } while (0) 91 | #endif /* PICORBC_DEBUG */ 92 | 93 | #endif /* PICORBC_DEBUG_H_ */ 94 | -------------------------------------------------------------------------------- /include/scope.h: -------------------------------------------------------------------------------- 1 | #ifndef PICORBC_SCOPE_H_ 2 | #define PICORBC_SCOPE_H_ 3 | 4 | #include 5 | 6 | #define MRB_HEADER_SIZE 32 7 | #define MRB_FOOTER_SIZE 8 8 | #define IREP_HEADER_SIZE 16 9 | 10 | typedef enum literal_type 11 | { 12 | STRING_LITERAL = 0, 13 | INT32_LITERAL = 1, 14 | SSTRING_LITERAL = 2, 15 | INT64_LITERAL = 3, 16 | FLOAT_LITERAL = 5, 17 | BIGINT_LITERAL = 7 18 | } LiteralType; 19 | 20 | #define IREP_TT_NFLAG 1 /* number (non string) flag */ 21 | #define IREP_TT_SFLAG 2 /* static string flag */ 22 | 23 | typedef struct literal 24 | { 25 | uint32_t type; /* LiteralType and lenght(<<2) when it's a STRING */ 26 | const char *value; 27 | struct literal *next; 28 | } Literal; 29 | 30 | typedef struct gen_literal 31 | { 32 | const char *value; 33 | struct gen_literal *prev; 34 | } GenLiteral; 35 | 36 | /* 37 | * A symbol can be: 38 | * @ivar, $gvar, puts(fname), :symbol, CONST 39 | */ 40 | typedef struct symbol 41 | { 42 | uint32_t len; /* used in dump.c */ 43 | const char *value; 44 | struct symbol *next; 45 | } Symbol; 46 | 47 | typedef struct lvar 48 | { 49 | uint32_t len; 50 | const char *name; 51 | struct lvar *next; 52 | uint8_t regnum; 53 | bool to_be_free; 54 | } Lvar; /* will be casted as Symbol in dump.c */ 55 | 56 | typedef struct lvar_scope_reg 57 | { 58 | uint8_t scope_num; 59 | uint8_t reg_num; 60 | } LvarScopeReg; 61 | 62 | #define CODE_POOL_SIZE 25 63 | typedef struct code_pool 64 | { 65 | struct code_pool *next; // 4 bytes or 8 bytes 66 | uint16_t size; // 2 bytes 67 | uint8_t index; // 1 bytes 68 | uint8_t data[CODE_POOL_SIZE]; // 25 bytes (Note: It can be bigger) 69 | // => 32 bytes total in 32 bit 70 | // or 40 bytes total in 64 bit 71 | } CodePool; 72 | 73 | /* 74 | * For symbols which aren't stored in ParserState's string_pool 75 | * like `[]=` `attr=` 76 | * They should be created in generator.c 77 | */ 78 | typedef struct assign_symbol 79 | { 80 | struct assign_symbol *prev; 81 | const char *value; 82 | } AssignSymbol; 83 | 84 | typedef struct exception_handler ExcHandler; 85 | typedef struct exception_handler 86 | { 87 | uint8_t table[13]; 88 | ExcHandler *next; 89 | } ExcHandler; 90 | 91 | typedef struct scope Scope; 92 | typedef struct generator_state GeneratorState; 93 | typedef struct scope 94 | { 95 | uint32_t irep_parameters; /* bbb */ 96 | uint32_t nest_stack; /* Initial: 00000000 00000000 00000000 00000001 */ 97 | Scope *upper; 98 | Scope *first_lower; 99 | Scope *next; 100 | bool lvar_top; 101 | uint16_t next_lower_number; 102 | uint16_t nlowers; /* irep.rlen in mruby/c (num of child IREP block) */ 103 | CodePool *first_code_pool; 104 | CodePool *current_code_pool; 105 | unsigned int nlocals; 106 | Symbol *symbol; 107 | Lvar *lvar; 108 | Literal *literal; 109 | GenLiteral *gen_literal; /* Exceptional literals in generator */ 110 | uint16_t sp; 111 | uint16_t rlen; /* reg length */ 112 | uint32_t ilen; /* irep length */ 113 | uint16_t slen; /* symbol length */ 114 | uint16_t plen; /* pool length */ 115 | uint16_t clen; /* exception handler length */ 116 | uint32_t vm_code_size; 117 | uint8_t *vm_code; 118 | uint8_t block_arg_regnum; 119 | AssignSymbol *last_assign_symbol; 120 | ExcHandler *exc_handler; 121 | GeneratorState *g; 122 | } Scope; 123 | 124 | #define GEN_SPLAT_STATUS_NONE 0b00000000 125 | #define GEN_SPLAT_STATUS_BEFORE_SPLAT 0b00000001 126 | #define GEN_SPLAT_STATUS_AFTER_SPLAT 0b00000010 127 | #define GEN_ARRAY_STATUS_NONE 0b00000000 128 | #define GEN_ARRAY_STATUS_GENERATING 0b00000001 129 | #define GEN_ARRAY_STATUS_GENERATING_SPLIT 0b00000010 130 | 131 | Scope *Scope_new(Scope *upper, bool lvar_top); 132 | 133 | void Scope_free(Scope *self); 134 | 135 | void Scope_pushNCode_self(Scope *self, uint8_t *value, int size); 136 | #define Scope_pushNCode(v, s) Scope_pushNCode_self(scope, (v), (s)) 137 | 138 | void Scope_pushCode_self(Scope *self, int val); 139 | #define Scope_pushCode(v) Scope_pushCode_self(scope, (v)) 140 | 141 | int Scope_newLit(Scope *self, const char *value, LiteralType type); 142 | 143 | int Scope_newSym(Scope *self, const char *value); 144 | 145 | int Scope_assignSymIndex(Scope *self, const char *method_name); 146 | 147 | LvarScopeReg Scope_lvar_findRegnum(Scope *self, const char *name); 148 | 149 | void Scope_newLvar(Scope *self, const char *name, int newRegnum); 150 | 151 | void Scope_setSp(Scope *self, uint16_t sp); 152 | 153 | void Scope_push(Scope *self); 154 | 155 | void Scope_finish(Scope *self); 156 | 157 | void Scope_freeCodePool(Scope *self); 158 | 159 | void Scope_freeLvar(Lvar *); 160 | 161 | int Scope_updateVmCodeSizeThenReturnTotalSize(Scope *self); 162 | 163 | #endif 164 | -------------------------------------------------------------------------------- /test/fixtures/keymap.rb: -------------------------------------------------------------------------------- 1 | # Initialize a Keyboard 2 | kbd = Keyboard.new 3 | 4 | # `split=` should happen before `init_pins` 5 | kbd.split = true 6 | 7 | # Caution!!!! 8 | # - Keyboard#mutual_uart_at_my_own_risk is experimental. 9 | # It may make the keyboard unstable and may break your chip 10 | # - Make sure to disconnect the TRS(TRRS) cable when you change 11 | # this configuration. Reconnect them after changing BOTH halves 12 | # - Keys RGB_xxx are working on partner half only using this option 13 | kbd.mutual_uart_at_my_own_risk = true 14 | 15 | # If your right hand of CRKBD is the "anchor" 16 | kbd.set_anchor(:right) 17 | 18 | # Initialize GPIO assign 19 | kbd.init_pins( 20 | [ 4, 5, 6, 7 ], # row0, row1,... respectively 21 | [ 29, 28, 27, 26, 22, 20 ] # col0, col1,... respectively 22 | ) 23 | 24 | # default layer should be added at first 25 | kbd.add_layer :default, %i[ 26 | KC_ESCAPE KC_Q KC_W KC_E KC_R KC_T KC_Y KC_U KC_I KC_O KC_P KC_MINUS 27 | KC_TAB KC_A KC_S KC_D KC_F KC_G KC_H KC_J KC_K KC_L KC_SCOLON KC_BSPACE 28 | KC_LSFT KC_Z KC_X KC_C KC_V KC_B KC_N KC_M KC_COMMA KC_DOT KC_SLASH KC_RSFT 29 | KC_NO KC_NO KC_NO ALT_AT KC_LCTL LOWER_SPC RAISE_ENT SPC_CTL RUBY_GUI KC_NO KC_NO KC_NO 30 | ] 31 | kbd.add_layer :raise, %i[ 32 | KC_GRAVE KC_EXLM KC_AT KC_HASH KC_DLR KC_PERC KC_CIRC KC_AMPR KC_ASTER KC_LPRN KC_RPRN KC_EQUAL 33 | KC_TAB KC_LABK KC_LCBR KC_LBRACKET KC_LPRN KC_QUOTE KC_LEFT KC_DOWN KC_UP KC_RIGHT KC_UNDS KC_PIPE 34 | KC_LSFT KC_RABK KC_RCBR KC_RBRACKET KC_RPRN KC_DQUO KC_TILD KC_BSLASH KC_COMMA KC_DOT KC_SLASH KC_RSFT 35 | KC_NO KC_NO KC_NO ALT_AT KC_LCTL ADJUST RAISE_ENT SPC_CTL KC_RGUI KC_NO KC_NO KC_NO 36 | ] 37 | kbd.add_layer :lower, %i[ 38 | KC_ESCAPE KC_1 KC_2 KC_3 KC_4 KC_5 KC_6 KC_7 KC_8 KC_9 KC_0 KC_EQUAL 39 | KC_TAB KC_NO KC_NO KC_NO KC_LPRN KC_QUOTE KC_DOT KC_4 KC_5 KC_6 KC_PLUS KC_BSPACE 40 | KC_LSFT KC_RABK KC_RCBR KC_RBRACKET KC_RPRN KC_DQUO KC_0 KC_1 KC_2 KC_3 KC_SLASH KC_COMMA 41 | KC_NO KC_NO KC_NO ALT_AT KC_LCTL LOWER_SPC ADJUST SPC_CTL KC_RGUI KC_NO KC_NO KC_NO 42 | ] 43 | kbd.add_layer :adjust, %i[ 44 | KC_F1 KC_F2 KC_F3 KC_F4 KC_F5 KC_F6 KC_F7 KC_F8 KC_F9 KC_F10 KC_F11 KC_F12 45 | RGB_TOG RGB_SPI RGB_HUI RGB_SAI RGB_VAI RGB_MOD KC_NO KC_NO KC_NO KC_NO KC_NO KC_NO 46 | RGB_TOG RGB_SPD RGB_HUD RGB_SAD RGB_VAD RGB_RMOD KC_NO KC_NO KC_NO KC_NO KC_NO KC_NO 47 | KC_NO KC_NO KC_NO ALT_AT KC_LCTL ADJUST ADJUST SPC_CTL KC_RGUI KC_NO KC_NO KC_NO 48 | ] 49 | 50 | kbd.define_composite_key :SPC_CTL, %i(KC_SPACE KC_RCTL) 51 | 52 | # 53 | # Your custom Keycode or Keycode (only modifiers) Release time Re-push time 54 | # key name Array of Keycode or Layer Symbol to be held threshold(ms) threshold(ms) 55 | # or Proc or Proc which will run to consider as to consider as 56 | # when you click while you keep press `click the key` `hold the key` 57 | kbd.define_mode_key :ALT_AT, [ :KC_AT, :KC_LALT, 150, 150 ] 58 | kbd.define_mode_key :RAISE_ENT, [ :KC_ENTER, :raise, 150, 150 ] 59 | kbd.define_mode_key :LOWER_SPC, [ :KC_SPACE, :lower, 150, 150 ] 60 | kbd.define_mode_key :RUBY_GUI, [ Proc.new { kbd.ruby }, :KC_RGUI, 300, nil ] 61 | kbd.define_mode_key :ADJUST, [ nil, :adjust, nil, nil ] 62 | 63 | # `before_report` will work just right before reporting what keys are pushed to USB host. 64 | # You can use it to hack data by adding an instance method to Keyboard class by yourself. 65 | # ex) Use Keyboard#before_report filter if you want to input `":" w/o shift` and `";" w/ shift` 66 | #kbd.before_report do 67 | # kbd.invert_sft if kbd.keys_include?(:KC_SCOLON) 68 | # # You'll be also able to write `invert_ctl`, `invert_alt` and `invert_gui` 69 | #end 70 | 71 | # Initialize RGBLED with pin, underglow_size, backlight_size and is_rgbw. 72 | rgb = RGB.new( 73 | 0, # pin number 74 | 6, # size of underglow pixel 75 | 21, # size of backlight pixel 76 | false # 32bit data will be sent to a pixel if true while 24bit if false 77 | ) 78 | # Set an effect 79 | # `nil` or `:off` for turning off 80 | rgb.effect = :swirl 81 | # rgb.effect = :rainbow_mood 82 | # Set an action when you input 83 | # `nil` or `:off` for turning off 84 | # rgb.action = :thunder 85 | # Append the feature. Will possibly be able to write `Keyboard#append(OLED.new)` in the future 86 | kbd.append rgb 87 | 88 | kbd.start! 89 | -------------------------------------------------------------------------------- /include/parse_header.h: -------------------------------------------------------------------------------- 1 | #ifndef LEMON_PARSE_HEADER_H_ 2 | #define LEMON_PARSE_HEADER_H_ 3 | 4 | #include 5 | 6 | #include "scope.h" 7 | #include "token.h" 8 | #ifndef PICORBC_PTR_SIZE 9 | #include 10 | #endif 11 | 12 | typedef enum atom_type { 13 | ATOM_NONE = 0, 14 | ATOM_program = 1, 15 | ATOM_method_add_arg, 16 | ATOM_kw_nil, 17 | ATOM_kw_true, 18 | ATOM_kw_false, 19 | ATOM_lvar, 20 | ATOM_array, 21 | ATOM_hash, 22 | ATOM_kw_hash, 23 | ATOM_assoc_new, 24 | ATOM_assoc_key, 25 | ATOM_assoc_value, 26 | ATOM_call, 27 | ATOM_scall, 28 | ATOM_fcall, 29 | ATOM_vcall, 30 | ATOM_command, 31 | ATOM_masgn, 32 | ATOM_assign, 33 | ATOM_assign_backpatch, 34 | ATOM_op_assign, 35 | ATOM_at_op, 36 | ATOM_and, 37 | ATOM_or, 38 | ATOM_var_field, 39 | ATOM_var_ref, 40 | ATOM_dstr, 41 | ATOM_str, 42 | ATOM_dstr_add, 43 | ATOM_dstr_new, 44 | ATOM_string_content, 45 | ATOM_mrhs, 46 | ATOM_mlhs, 47 | ATOM_mlhs_pre, 48 | ATOM_mlhs_rest, 49 | ATOM_mlhs_post, 50 | ATOM_args_new, 51 | ATOM_args_add, 52 | ATOM_args_add_block, 53 | ATOM_block_arg, 54 | ATOM_kw_self, 55 | ATOM_kw_return, 56 | ATOM_kw_yield, 57 | ATOM_at_int, 58 | ATOM_at_float, 59 | ATOM_stmts_add, 60 | ATOM_string_literal, 61 | ATOM_symbol_literal, 62 | ATOM_dsymbol, 63 | ATOM_binary, 64 | ATOM_unary, 65 | ATOM_stmts_new, 66 | ATOM_at_ident, 67 | ATOM_at_ivar, 68 | ATOM_at_gvar, 69 | ATOM_at_const, 70 | ATOM_at_tstring_content, 71 | ATOM_if, 72 | ATOM_while, 73 | ATOM_until, 74 | ATOM_case, 75 | ATOM_case_body, 76 | ATOM_break, 77 | ATOM_next, 78 | ATOM_redo, 79 | ATOM_block, 80 | ATOM_def, 81 | ATOM_sdef, 82 | ATOM_block_parameters, 83 | ATOM_arg, 84 | ATOM_margs, 85 | ATOM_optargs, 86 | ATOM_m2args, 87 | ATOM_args_tail, 88 | ATOM_args_tail_kw_arg, 89 | ATOM_args_tail_kw_rest_args, 90 | ATOM_args_tail_block, 91 | ATOM_restarg, 92 | ATOM_class, 93 | ATOM_sclass, 94 | ATOM_module, 95 | ATOM_alias, 96 | ATOM_dot2, 97 | ATOM_dot3, 98 | ATOM_colon2, 99 | ATOM_colon3, 100 | ATOM_splat, 101 | ATOM_super, 102 | ATOM_zsuper, 103 | ATOM_lambda, 104 | ATOM_keyword_rest_args, 105 | ATOM_rescue, 106 | ATOM_exc_list, 107 | ATOM_exc_var, 108 | ATOM_another_rescue, 109 | ATOM_ensure, 110 | ATOM_retry, 111 | } AtomType; 112 | 113 | typedef enum { 114 | ATOM, 115 | CONS, 116 | LITERAL, 117 | } NodeType; 118 | 119 | typedef struct node Node; 120 | 121 | typedef struct { 122 | struct node *car; 123 | struct node *cdr; 124 | } Cons; 125 | 126 | typedef struct { 127 | AtomType type; 128 | } Atom; 129 | 130 | typedef struct { 131 | const char *name; 132 | } Value; 133 | 134 | struct node { 135 | NodeType type; 136 | union { 137 | Atom atom; 138 | Cons cons; 139 | Value value; 140 | }; 141 | }; 142 | 143 | typedef struct node_box NodeBox; 144 | 145 | typedef struct node_box 146 | { 147 | NodeBox *next; 148 | uint16_t size; 149 | uint16_t index; 150 | Node *nodes; 151 | } NodeBox; 152 | 153 | #define STRING_POOL_SIZE (PICORBC_PTR_SIZE * 16) 154 | #define STRING_POOL_HEADER_SIZE (PICORBC_PTR_SIZE * 3) 155 | #define STRING_POOL_POOL_SIZE (STRING_POOL_SIZE - STRING_POOL_HEADER_SIZE) 156 | typedef struct string_pool StringPool; 157 | typedef struct string_pool 158 | { 159 | StringPool *prev; 160 | uint16_t size; /* maximum size of strings */ 161 | uint16_t index; /* current size + 1 of strings */ 162 | char strings[STRING_POOL_POOL_SIZE]; 163 | } StringPool; 164 | 165 | typedef struct specail_string_pool { 166 | char null[1]; /* "" */ 167 | char neg[2]; /* "-" */ 168 | char ary[3]; /* "[]" */ 169 | } SpecialStringPool;; 170 | 171 | typedef enum paren 172 | { 173 | PAREN_NONE, 174 | PAREN_PAREN, 175 | PAREN_BRACE, 176 | } Paren; 177 | 178 | /* margin size to malloc tokenizer */ 179 | #define PAREN_STACK_SIZE 40 180 | 181 | typedef struct parser_state { 182 | Scope *scope; 183 | NodeBox *root_node_box; 184 | NodeBox *current_node_box; 185 | uint8_t node_box_size; 186 | StringPool *current_string_pool; 187 | SpecialStringPool special_string_pool; 188 | unsigned int error_count; 189 | unsigned int cond_stack; 190 | unsigned int cmdarg_stack; 191 | bool cmd_start; 192 | State state; 193 | bool verbose; 194 | int paren_stack_num; 195 | Paren paren_stack[PAREN_STACK_SIZE]; 196 | unsigned int lpar_beg; 197 | int in_def; 198 | int in_single; 199 | } ParserState; 200 | 201 | #define BITSTACK_PUSH(stack, n) ((stack) = ((stack) << 1) | ((n) & 1)) 202 | #define BITSTACK_POP(stack) ((stack) = (stack) >> 1) 203 | #define BITSTACK_LEXPOP(stack) ((stack) = ((stack) >> 1) | ((stack) & 1)) 204 | #define BITSTACK_SET_P(stack) ((stack) & 1) 205 | 206 | #define COND_PUSH(n) BITSTACK_PUSH(p->cond_stack, (n)) 207 | #define COND_POP() BITSTACK_POP(p->cond_stack) 208 | #define COND_LEXPOP() BITSTACK_LEXPOP(p->cond_stack) 209 | #define COND_P() BITSTACK_SET_P(p->cond_stack) 210 | 211 | #define CMDARG_PUSH(n) BITSTACK_PUSH(p->cmdarg_stack, (n)) 212 | #define CMDARG_POP() BITSTACK_POP(p->cmdarg_stack) 213 | #define CMDARG_LEXPOP() BITSTACK_LEXPOP(p->cmdarg_stack) 214 | #define CMDARG_P() BITSTACK_SET_P(p->cmdarg_stack) 215 | 216 | //#define sym(x) ((mrb_sym)(intptr_t)(x)) 217 | #define nsym(x) ((Node*)(intptr_t)(x)) 218 | #define nint(x) ((Node*)(intptr_t)(x)) 219 | #define intn(x) ((int)(intptr_t)(x)) 220 | //#define typen(x) ((enum node_type)(intptr_t)(x)) 221 | #endif 222 | -------------------------------------------------------------------------------- /lib/Semantic-value-of-mid-rule-action-in-Lemon.md: -------------------------------------------------------------------------------- 1 | # Semantic value of mid-rule action in Lemon 2 | 3 | Unlike Yacc/Bison, Lemon doesn't have the mid-rule action (MRA) feature that allows you to write an action anywhere in the middle of a right-hand side (RHS). 4 | 5 | ## Case 1 - Basic 6 | 7 | Let's say you are defining a grammar rule for Ruby's begin-end expression: 8 | 9 | ```ruby 10 | begin 11 | do_something # bodystmt 12 | end 13 | ``` 14 | 15 | ```c 16 | /* Yacc/Bison */ 17 | primary : keyword_begin /* $1 */ 18 | { 19 | $$ = p->cmdarg_stack; 20 | p->cmdarg_stack = 0; 21 | } /* $2 👀 */ 22 | bodystmt /* $3 */ 23 | keyword_end /* $4 */ 24 | { 25 | p->cmdarg_stack = $2; 26 | $$ = $3; 27 | } 28 | ``` 29 | 30 | The MRA preserves a value of `p->cmdarg_stack` as a variable named `$$` because `p->cmdarg_stack` should be reset to zero so that `bodystmt` can begin a new statement. 31 | Eventually, at the finalizing action of the rule, `p->cmdarg_stack` is restored by referring `$2` variable. 32 | The last digit `2` of the variable is the semantic value number `2` as the MRA has its own semantic value number in Yacc/Bison. 33 | 34 | In Lemon, you can write the equivalent rule as follows: 35 | 36 | ```c 37 | /* Lemon */ 38 | primary(A) ::= keyword_begin 39 | preserve_cmdarg_stach(STACK) 40 | bodystmt(B) 41 | keyword_end. 42 | { 43 | p->cmdarg_stack = STACK; 44 | A = B; 45 | } 46 | preserve_cmdarg_stach(STACK) ::= . 47 | { 48 | STACK = p->cmdarg_stack; 49 | p->cmdarg_stack = 0; 50 | } 51 | ``` 52 | 53 | Preserving `p->cmdarg_stack` can be written in `preserve_cmdarg_stach` with an empty RHS. 54 | Actually, MRA of Yacc/Bison is just a syntax sugar of this. 55 | 56 | ## Case 2 - Semantic value right after a terminal 57 | 58 | You are trying to write another rule for "arrow-style" lambda this time: 59 | 60 | ```ruby 61 | -> (arg) { arg.do_someting } 62 | ``` 63 | 64 | In Yacc/Bison, you can write the rule like this: 65 | 66 | ```c 67 | /* Yacc/Bison */ 68 | primary : tLAMBDA /* $1 */ 69 | { 70 | local_nest(p); 71 | nvars_nest(p); 72 | $$ = p->lpar_beg; 73 | p->lpar_beg = ++p->paren_nest; 74 | } /* $2 */ 75 | f_larglist /* $3 */ 76 | { 77 | $$ = p->cmdarg_stack; 78 | p->cmdarg_stack = 0; 79 | } /* $4 */ 80 | lambda_body /* $5 */ 81 | { 82 | p->lpar_beg = $2; 83 | $$ = new_lambda(p, $3, $5); 84 | local_unnest(p); 85 | nvars_unnest(p); 86 | p->cmdarg_stack = $4; 87 | CMDARG_LEXPOP(); 88 | } 89 | ``` 90 | 91 | ```c 92 | /* Lemon */ 93 | primary(A) ::= lambda_head(NUM) LAMBDA f_larglist(B) preserve_cmdarg_stach(STACK) lambda_body(C). 94 | /* ^^^^^^^^^^^^^^^^^^^^^^^👀 */ 95 | { 96 | p->lpar_beg = NUM; 97 | A = new_lambda(p, B, C); 98 | local_unnest(p); 99 | nvars_unnest(p); 100 | p->cmdarg_stack = STACK; 101 | CMDARG_LEXPOP(); 102 | } 103 | lambda_head(NUM) ::= . 104 | { 105 | local_nest(p); 106 | nvars_nest(p); 107 | NUM = p->lpar_beg; 108 | p->lpar_beg = ++p->paren_nest; 109 | } 110 | preserve_cmdarg_stach(STACK) ::= . 111 | { 112 | STACK = p->cmdarg_stack; 113 | p->cmdarg_stack = 0; 114 | } 115 | ``` 116 | 117 | There is only one thing that you should pay attention, although this rule is intricate. 118 | It is that, in Lemon, `lambda_head(NUM)` is located **before** `LAMBDA`. 119 | But why? 120 | 121 | Remember that Lemon is an LALR(1) parser. 122 | A **Look-Ahead** LR parser looks ahead one more token before reduction. 123 | 124 | If you write `LAMBDA lambda_head(NUM)` in the opposite order, there are two scenarios that don't work: 125 | 126 | 1. `-> (arg) { arg.do_something }` 127 | In this scenario, the action of `lambda_head` will happen after inputting `(` (left paren of `(args)`). 128 | This causes inconsistency of `p->paren_nest` status at the point that our tokenizer finds a token `{` which starts `lambda_body`. 129 | 130 | 2. `-> { do_something }` 131 | In this scenario, invoking the action of `lambda_head` is delayed until inputting a token `{`. 132 | This is neither what you expect because preserving `NUM = p->lpar_beg;` should happen before the tokenizer finds `{`. 133 | 134 | The difference between Case 1 and 2 is what kind of effect you expect from MRA. 135 | If you want the MRA to change the parser status of which the tokenizer takes advantage, you may have to put the MRA by taking the look-ahead behavior into consideration. 136 | 137 | ## Conclusion so far 138 | 139 | Things are so timing-dependant that you have to be careful to use semantic values of MRA in Lemon. 140 | The conclusion is that it would be better to see debug prints😅 141 | 142 | ---- 143 | 144 | This document likely contains some wrong points. Please tell twitter.com/hasumikin if you noticed something. 145 | -------------------------------------------------------------------------------- /src/compiler.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "parse.c" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define ZERO "\e[30;1m" 18 | #define CTRL "\e[35;1m" 19 | #define LETTER "\e[36m" 20 | #define EIGHTBIT "\e[33m" 21 | #define DUMP_LINE_LEN 210 22 | void dumpCode(Scope *scope) 23 | { 24 | char line[DUMP_LINE_LEN]; 25 | memset(line, '\0', DUMP_LINE_LEN); 26 | int linelen = 0; 27 | int c, i, j; 28 | for (i = 0; i < scope->vm_code_size; i++) { 29 | c = scope->vm_code[i]; 30 | if (linelen == 8) strsafecat(line, "|", DUMP_LINE_LEN); 31 | if (i != 0) { 32 | if (i % 16 == 0) { 33 | printf(" %s\n", line); 34 | linelen = 0; 35 | memset(line, '\0', 18); 36 | } else if (i % 8 == 0) { 37 | printf("| "); 38 | } 39 | } 40 | if (c == 0) { 41 | printf(ZERO); 42 | strsafecat(line, ZERO, DUMP_LINE_LEN); 43 | strsafecat(line, "0", DUMP_LINE_LEN); 44 | } else if (c < 0x20) { 45 | printf(CTRL); 46 | strsafecat(line, CTRL, DUMP_LINE_LEN); 47 | strsafecat(line, "\uFFED", DUMP_LINE_LEN); 48 | } else if (c < 0x7f) { 49 | printf(LETTER); 50 | strsafecat(line, LETTER, DUMP_LINE_LEN); 51 | strsafecat(line, (char *)&c, DUMP_LINE_LEN); 52 | } else { 53 | printf(EIGHTBIT); 54 | strsafecat(line, EIGHTBIT, DUMP_LINE_LEN); 55 | strsafecat(line, "\uFFED", DUMP_LINE_LEN); 56 | } 57 | strsafecat(line, "\e[m", DUMP_LINE_LEN); 58 | linelen++; 59 | printf("%02x\e[m ", c); 60 | } 61 | if (linelen != 16) { 62 | if (i % 16 < 9) printf(" "); /* equiv size to "| " */ 63 | for (j = i % 16; j < 16; j++) printf(" "); 64 | } 65 | printf(" %s\n", line); 66 | } 67 | 68 | void 69 | printToken(Tokenizer *tokenizer, Token *token) { 70 | printf("\e[32;40;1mtoken> %s\e[m \e[35;40;1m%s\e[m `\e[31;40;1m%s\e[m` \e[36;40;1m%s\e[m len=%zu line=%d pos=%d\n", 71 | tokenizer_mode_name(tokenizer->mode), 72 | token_name(token->type), 73 | token->value, 74 | tokenizer_state_name(token->state), 75 | strlen(token->value), 76 | token->line_num, 77 | token->pos); 78 | } 79 | 80 | static inline void 81 | save_context(ParserState *p, picorbc_context *cxt) 82 | { 83 | if (!cxt) return; 84 | Lvar *lvar = p->scope->lvar; 85 | unsigned int nlocals; 86 | for (nlocals = 0; lvar; nlocals++) { 87 | if (!lvar->to_be_free) { 88 | char *name = picorbc_alloc(lvar->len + 1); 89 | strncpy(name, lvar->name, lvar->len + 1); 90 | lvar->name = name; 91 | lvar->to_be_free = true; 92 | } 93 | lvar = lvar->next; 94 | } 95 | cxt->slen = nlocals; 96 | cxt->syms = (mrb_sym *)p->scope->lvar; 97 | p->scope->lvar = NULL; 98 | } 99 | 100 | static inline void 101 | restore_context(ParserState *p, picorbc_context *cxt) 102 | { 103 | if (!cxt) return; 104 | p->scope->nlocals += cxt->slen; 105 | p->scope->lvar = (Lvar *)cxt->syms; 106 | p->scope->sp += cxt->slen; 107 | } 108 | 109 | bool Compiler_compile(ParserState *p, StreamInterface *si, picorbc_context *cxt) 110 | { 111 | restore_context(p, cxt); 112 | /* unusing global_preg_cache prefers smaller RAM consumption */ 113 | MyRegex_setup(false); 114 | /* using global_preg_cache prefers fewer number of step */ 115 | // MyRegex_setup(true); 116 | Tokenizer *tokenizer = Tokenizer_new(p, si); 117 | Token *topToken = tokenizer->currentToken; 118 | yyParser *parser = ParseAlloc(picorbc_alloc, p); 119 | #ifdef PICORUBY_DEBUG 120 | if (p->verbose) { 121 | ParseTrace(stdout, (char *)"parse> "); 122 | } 123 | #endif 124 | Type prevType = 0; 125 | while( Tokenizer_hasMoreTokens(tokenizer) ) { 126 | if (Tokenizer_advance(tokenizer, p, false) > 0) { 127 | p->error_count++; 128 | break; 129 | } 130 | for (;;) { 131 | if (topToken->value == NULL) { 132 | DEBUGP("(main)%p null", topToken); 133 | } else { 134 | if (topToken->type != ON_SP) { 135 | if (p->verbose) printToken(tokenizer, topToken); 136 | const char *string; 137 | switch (topToken->type) { 138 | case IVAR: 139 | case GVAR: 140 | case CHAR: 141 | case LABEL: 142 | case INTEGER: 143 | case FLOAT: 144 | case IDENTIFIER: 145 | case CONSTANT: 146 | case STRING: 147 | case OP_ASGN: 148 | case DSTRING_TOP: 149 | case DSTRING_MID: 150 | string = ParsePushStringPool(p, topToken->value); 151 | break; 152 | case COMMENT: 153 | string = NULL; 154 | break; 155 | default: 156 | string = topToken->value; 157 | break; 158 | } 159 | if ((prevType == DSTRING_END || prevType == STRING_BEG) 160 | && topToken->type == STRING_END) { 161 | Parse(parser, STRING, ""); /* to help parser */ 162 | } 163 | if (topToken->type != STRING_END && topToken->type != COMMENT) { 164 | Parse(parser, topToken->type, string); 165 | } 166 | if (p->error_count != 0) { 167 | Token_free(topToken); 168 | goto FAIL; 169 | } 170 | prevType = topToken->type; 171 | } 172 | } 173 | if (topToken->next == NULL) { 174 | break; 175 | } else { 176 | topToken->refCount--; 177 | topToken = topToken->next; 178 | } 179 | } 180 | } 181 | Parse(parser, 0, ""); 182 | FAIL: 183 | MyRegexCache_free(); 184 | bool result; 185 | if (p->error_count == 0) { 186 | result = true; 187 | if (p->verbose) ParseShowAllNode(parser, 1); 188 | Generator_generate(p->scope, p->root_node_box->nodes, p->verbose); 189 | save_context(p, cxt); 190 | } else { 191 | // FIXME should print prev line 192 | ERRORP("Syntax error at line:%d", tokenizer->line_num); 193 | result = false; 194 | } 195 | if (p->verbose && result) dumpCode(p->scope); 196 | ParseFreeAllNode(parser); 197 | picorbc_free(parser); 198 | Tokenizer_free(tokenizer); 199 | return result; 200 | } 201 | 202 | ParserState * 203 | Compiler_parseInitState(ParserState *p, uint8_t node_box_size) 204 | { 205 | if (p == NULL) { 206 | p = ParseInitState(0, node_box_size); 207 | } else { 208 | ParseInitState(p, node_box_size); 209 | } 210 | return p; 211 | } 212 | 213 | void 214 | Compiler_parserStateFree(ParserState *p) 215 | { 216 | ParserStateFree(p); 217 | } 218 | -------------------------------------------------------------------------------- /mrbgem.rake: -------------------------------------------------------------------------------- 1 | MRuby::Gem::Specification.new('mruby-pico-compiler') do |spec| 2 | spec.license = 'MIT' 3 | spec.authors = 'HASUMI Hitoshi' 4 | spec.summary = 'small footprint mruby compiler library' 5 | 6 | spec.add_conflict 'mruby-compiler' 7 | 8 | if build.gems['picoruby-mrubyc'] 9 | begin 10 | Rake::Task["#{build.gems['picoruby-mrubyc'].dir}/repos/mrubyc"].invoke 11 | rescue RuntimeError => e 12 | # ignore 13 | end 14 | end 15 | 16 | include_dir = "#{dir}/include" 17 | src_dir = "#{dir}/src" 18 | lib_dir = "#{dir}/lib" 19 | spec.cc.include_paths << include_dir 20 | 21 | Dir.glob("#{src_dir}/*.c").each do |src| 22 | file objfile(src.pathmap "#{build_dir}/src/%n") => [src, "#{include_dir}/parse_header.h"] do |f| 23 | cc.run f.name, src 24 | end 25 | end 26 | 27 | objs = %w[parse compiler tokenizer common context dump generator microrb mrbgem my_regex node regex scope stream token].map do |name| 28 | src = "#{src_dir}/#{name}.c" 29 | if build.cxx_exception_enabled? 30 | build.compile_as_cxx(src) 31 | else 32 | objfile(src.pathmap("#{build_dir}/src/%n")) 33 | end 34 | end 35 | 36 | if cc.defines.include?("DISABLE_MRUBY") 37 | build.libmruby_core_objs.clear 38 | end 39 | build.libmruby_core_objs << objs 40 | 41 | directory include_dir 42 | 43 | file "#{include_dir}/keyword_helper.h" => [include_dir, "#{include_dir}/parse.h"] do |t| 44 | File.open(t.name, "w") do |file| 45 | file.puts <<~TEXT 46 | #include "parse.h" 47 | int8_t 48 | keyword(const char *word) 49 | { 50 | TEXT 51 | File.open("#{include_dir}/parse.h", "r") do |f| 52 | f.each_line do |line| 53 | data = line.match(/\A#define\s+KW_(\w+)\s+\d+$/) 54 | if data && !data[1].match?('modifier_') && !%w(do_cond do_block).include?(data[1]) 55 | file.puts " if (!strcmp(word, \"#{data[1]}\")) { return KW_#{data[1]}; } else" 56 | end 57 | end 58 | end 59 | file.puts <<~TEXT 60 | { return -1; } 61 | } 62 | TEXT 63 | end 64 | end 65 | 66 | file "#{include_dir}/token_helper.h" => [include_dir, "#{include_dir}/parse.h"] do |t| 67 | File.open(t.name, "w") do |file| 68 | file.puts <<~TEXT 69 | inline static const char *token_name(int n) 70 | { 71 | switch(n) { 72 | TEXT 73 | File.open("#{include_dir}/parse.h", "r") do |f| 74 | f.each_line do |line| 75 | data = line.match(/\A#define\s+(\w+)\s+\d+$/) 76 | if data 77 | file.puts " case(#{data[1]}): return \"#{data[1]}\";" 78 | end 79 | end 80 | end 81 | file.puts <<~TEXT 82 | default: return "\\e[37;41;1m\\\"UNDEFINED\\\"\\e[m"; 83 | } 84 | } 85 | TEXT 86 | end 87 | end 88 | 89 | file "#{include_dir}/atom_helper.h" => [include_dir, "#{include_dir}/parse_header.h"] do |t| 90 | File.open(t.name, "w") do |file| 91 | file.puts <<~TEXT 92 | inline static const char *atom_name(AtomType n) 93 | { 94 | switch(n) { 95 | TEXT 96 | File.open("#{include_dir}/parse_header.h", "r") do |f| 97 | f.each_line do |line| 98 | break if line.match?("enum atom_type") 99 | end 100 | f.each_line do |line| 101 | break if line.match?("AtomType") 102 | data = line.match(/(\w+)(\s+=\s+\d+)?,?$/) 103 | if data 104 | file.puts " case(#{data[1]}): return \"#{data[1]}\";" 105 | end 106 | end 107 | end 108 | file.puts <<~TEXT 109 | default: return "\\e[37;41;1m\\\"UNDEFINED\\\"\\e[m"; 110 | } 111 | } 112 | TEXT 113 | end 114 | end 115 | 116 | file "#{include_dir}/tokenizer_helper.h" => [include_dir, "#{include_dir}/tokenizer.h", "#{include_dir}/token.h"] do |t| 117 | File.open(t.name, "w") do |file| 118 | file.puts <<~TEXT 119 | inline static const char *tokenizer_state_name(State n) 120 | { 121 | switch(n) { 122 | TEXT 123 | File.open("#{include_dir}/token.h", "r") do |f| 124 | f.each_line do |line| 125 | break if line.match?("enum state") 126 | end 127 | f.each_line do |line| 128 | break if line.match?("State") 129 | data = line.match(/\A\s+(\w+)\s+/) 130 | if data 131 | file.puts " case(#{data[1]}):#{' '*(13 - data[1].length)} return \"#{data[1]}\";" 132 | end 133 | end 134 | end 135 | file.puts <<~TEXT 136 | default: 137 | return "\\e[37;41;1mCOMBINED VALUE\\e[m"; 138 | } 139 | } 140 | TEXT 141 | file.puts "" 142 | file.puts <<~TEXT 143 | inline static const char *tokenizer_mode_name(Mode n) 144 | { 145 | switch(n) { 146 | TEXT 147 | File.open("#{include_dir}/tokenizer.h", "r") do |f| 148 | f.each_line do |line| 149 | break if line.match?("enum mode") 150 | end 151 | f.each_line do |line| 152 | break if line.match?("Mode") 153 | data = line.match(/\A\s+(\w+)/) 154 | if data 155 | file.puts " case(#{data[1]}):#{' '*(20 - data[1].length)} return \"#{data[1]}\";" 156 | end 157 | end 158 | end 159 | file.puts <<~TEXT 160 | default: return "\\e[37;41;1m\\\"UNDEFINED \\\"\\e[m"; 161 | } 162 | } 163 | TEXT 164 | end 165 | end 166 | 167 | file objfile("#{build_dir}/src/parse") => "#{src_dir}/parse.c" do |f| 168 | cc.run f.name, f.prerequisites.first 169 | end 170 | 171 | file "#{include_dir}/parse.h" => "#{src_dir}/parse.c" do |f| 172 | sh "cp #{f.prerequisites.first.pathmap("%X")}.h #{f}" 173 | end 174 | 175 | file "#{src_dir}/compiler.c" => %W(#{include_dir}/token_helper.h 176 | #{include_dir}/tokenizer_helper.h) do 177 | end 178 | 179 | file "#{src_dir}/tokenizer.c" => %W(#{include_dir}/token_helper.h 180 | #{include_dir}/keyword_helper.h) do 181 | end 182 | 183 | file "#{src_dir}/parse.c" => %W(#{include_dir} 184 | #{include_dir}/atom_helper.h 185 | #{src_dir}/parse.y 186 | #{lib_dir}/lemon 187 | #{include_dir}/ptr_size.h) do 188 | require "open3" 189 | flags = cc.defines.uniq.map{ |d| "-D#{d}" }.join(' ') 190 | cmd = "cd #{src_dir} && #{lib_dir}/lemon -p #{flags} ./parse.y" 191 | puts cmd 192 | out, err = Open3.capture3(cmd) 193 | puts out 194 | err.split("\n").each do |e| 195 | puts "\e[33;41;1m#{e}\e[m" 196 | unless e.include?("parsing conflict") 197 | raise "Parsing conflict" 198 | end 199 | end 200 | end 201 | 202 | file "#{lib_dir}/lemon" => %W(#{lib_dir}/lemon.c) do |f| 203 | command, flags = if cc.command.start_with?("arm-") # FIXME: This works only on x64 204 | cc.host_command || "gcc" 205 | else 206 | cc.command 207 | end 208 | sh "#{command} #{cc.flags.flatten.reject{|f|f.include?("-mcpu=") || f.include?("-mthumb")}.join(" ")} -o #{f.name} #{f.prerequisites.first}" 209 | end 210 | end 211 | -------------------------------------------------------------------------------- /include/opcode.h: -------------------------------------------------------------------------------- 1 | /*! @file 2 | @brief 3 | Define operation codes and associated macros. 4 | 5 |
  6 |   Copyright (C) 2015-2022 Kyushu Institute of Technology.
  7 |   Copyright (C) 2015-2022 Shimane IT Open-Innovation Center.
  8 | 
  9 |   This file is distributed under BSD 3-Clause License.
 10 | 
 11 | 
 12 |   
13 | */ 14 | 15 | #ifndef MRBC_SRC_OPCODE_H_ 16 | #define MRBC_SRC_OPCODE_H_ 17 | 18 | //@cond 19 | #include 20 | //@endcond 21 | 22 | #ifdef __cplusplus 23 | extern "C" { 24 | #endif 25 | 26 | 27 | #define FETCH_Z() (void)0 28 | 29 | #if defined(MRBC_SUPPORT_OP_EXT) 30 | #define FETCH_B() \ 31 | unsigned int a; \ 32 | a = *vm->inst++; if( ext & 1 ) a = a << 8 | *vm->inst++; \ 33 | (void)a 34 | 35 | #define FETCH_BB() \ 36 | unsigned int a, b; \ 37 | a = *vm->inst++; if( ext & 1 ) a = a << 8 | *vm->inst++; \ 38 | b = *vm->inst++; if( ext & 2 ) b = b << 8 | *vm->inst++; \ 39 | (void)a, (void)b 40 | 41 | #define FETCH_BBB() \ 42 | unsigned int a, b, c; \ 43 | a = *vm->inst++; if( ext & 1 ) a = a << 8 | *vm->inst++; \ 44 | b = *vm->inst++; if( ext & 2 ) b = b << 8 | *vm->inst++; \ 45 | c = *vm->inst++; \ 46 | (void)a, (void)b, (void)c 47 | 48 | #define FETCH_BS() \ 49 | unsigned int a, b; \ 50 | a = *vm->inst++; if( ext & 1 ) a = a << 8 | *vm->inst++; \ 51 | b = *vm->inst++; b = b << 8 | *vm->inst++; \ 52 | (void)a, (void)b 53 | 54 | #define FETCH_BSS() \ 55 | unsigned int a, b, c; \ 56 | a = *vm->inst++; if( ext & 1 ) a = a << 8 | *vm->inst++; \ 57 | b = *vm->inst++; b = b << 8 | *vm->inst++; \ 58 | c = *vm->inst++; c = c << 8 | *vm->inst++; \ 59 | (void)a, (void)b, (void)c 60 | 61 | #else 62 | #define FETCH_B() \ 63 | unsigned int a; \ 64 | a = *vm->inst++; \ 65 | (void)a 66 | 67 | #define FETCH_BB() \ 68 | unsigned int a, b; \ 69 | a = *vm->inst++; \ 70 | b = *vm->inst++; \ 71 | (void)a, (void)b 72 | 73 | #define FETCH_BBB() \ 74 | unsigned int a, b, c; \ 75 | a = *vm->inst++; \ 76 | b = *vm->inst++; \ 77 | c = *vm->inst++; \ 78 | (void)a, (void)b, (void)c 79 | 80 | #define FETCH_BS() \ 81 | unsigned int a, b; \ 82 | a = *vm->inst++; \ 83 | b = *vm->inst++; b = b << 8 | *vm->inst++; \ 84 | (void)a, (void)b 85 | 86 | #define FETCH_BSS() \ 87 | unsigned int a, b, c; \ 88 | a = *vm->inst++; \ 89 | b = *vm->inst++; b = b << 8 | *vm->inst++; \ 90 | c = *vm->inst++; c = c << 8 | *vm->inst++; \ 91 | (void)a, (void)b, (void)c 92 | 93 | #endif // defined(MRBC_SUPPORT_OP_EXT) 94 | 95 | #define FETCH_S() \ 96 | unsigned int a; \ 97 | a = *vm->inst++; a = a << 8 | *vm->inst++; \ 98 | (void)a 99 | 100 | #define FETCH_W() \ 101 | uint32_t a; \ 102 | a = *vm->inst++; a = a << 8 | *vm->inst++; a = a << 8 | *vm->inst++; \ 103 | (void)a 104 | 105 | 106 | //================================================================ 107 | /*!@brief 108 | Operation codes. 109 | 110 | operand types: 111 | Z: no operand 112 | B: 8bit (a) 113 | BB: 8+8bit (a,b) 114 | BBB: 8+8+8bit (a,b,c) 115 | BS: 8+16bit (a,b) 116 | BSS: 8+16+16bit (a,b,c) 117 | S: 16bit (a) 118 | W: 24bit (a) 119 | */ 120 | enum OPCODE { 121 | /*----------------------------------------------------------------------- 122 | operation code operands semantics 123 | ------------------------------------------------------------------------*/ 124 | OP_NOP = 0x00, //!< Z no operation 125 | OP_MOVE = 0x01, //!< BB R[a] = R[b] 126 | OP_LOADL = 0x02, //!< BB R[a] = Pool[b] 127 | OP_LOADI = 0x03, //!< BB R[a] = mrb_int(b) 128 | OP_LOADINEG = 0x04, //!< BB R[a] = mrb_int(-b) 129 | OP_LOADI__1 = 0x05, //!< B R[a] = mrb_int(-1) 130 | OP_LOADI_0 = 0x06, //!< B R[a] = mrb_int(0) 131 | OP_LOADI_1 = 0x07, //!< B R[a] = mrb_int(1) 132 | OP_LOADI_2 = 0x08, //!< B R[a] = mrb_int(2) 133 | OP_LOADI_3 = 0x09, //!< B R[a] = mrb_int(3) 134 | OP_LOADI_4 = 0x0A, //!< B R[a] = mrb_int(4) 135 | OP_LOADI_5 = 0x0B, //!< B R[a] = mrb_int(5) 136 | OP_LOADI_6 = 0x0C, //!< B R[a] = mrb_int(6) 137 | OP_LOADI_7 = 0x0D, //!< B R[a] = mrb_int(7) 138 | OP_LOADI16 = 0x0E, //!< BS R[a] = mrb_int(b) 139 | OP_LOADI32 = 0x0F, //!< BSS R[a] = mrb_int((b<<16)+c) 140 | OP_LOADSYM = 0x10, //!< BB R[a] = Syms[b] 141 | OP_LOADNIL = 0x11, //!< B R[a] = nil 142 | OP_LOADSELF = 0x12, //!< B R[a] = self 143 | OP_LOADT = 0x13, //!< B R[a] = true 144 | OP_LOADF = 0x14, //!< B R[a] = false 145 | OP_GETGV = 0x15, //!< BB R[a] = getglobal(Syms[b]) 146 | OP_SETGV = 0x16, //!< BB setglobal(Syms[b], R[a]) 147 | OP_GETSV = 0x17, //!< BB R[a] = Special[Syms[b]] 148 | OP_SETSV = 0x18, //!< BB Special[Syms[b]] = R[a] 149 | OP_GETIV = 0x19, //!< BB R[a] = ivget(Syms[b]) 150 | OP_SETIV = 0x1A, //!< BB ivset(Syms[b],R[a]) 151 | OP_GETCV = 0x1B, //!< BB R[a] = cvget(Syms[b]) 152 | OP_SETCV = 0x1C, //!< BB cvset(Syms[b],R[a]) 153 | OP_GETCONST = 0x1D, //!< BB R[a] = constget(Syms[b]) 154 | OP_SETCONST = 0x1E, //!< BB constset(Syms[b],R[a]) 155 | OP_GETMCNST = 0x1F, //!< BB R[a] = R[a]::Syms[b] 156 | OP_SETMCNST = 0x20, //!< BB R[a+1]::Syms[b] = R[a] 157 | OP_GETUPVAR = 0x21, //!< BBB R[a] = uvget(b,c) 158 | OP_SETUPVAR = 0x22, //!< BBB uvset(b,c,R[a]) 159 | OP_GETIDX = 0x23, //!< B R[a] = R[a][R[a+1]] 160 | OP_SETIDX = 0x24, //!< B R[a][R[a+1]] = R[a+2] 161 | OP_JMP = 0x25, //!< S pc+=a 162 | OP_JMPIF = 0x26, //!< BS if R[a] pc+=b 163 | OP_JMPNOT = 0x27, //!< BS if !R[a] pc+=b 164 | OP_JMPNIL = 0x28, //!< BS if R[a]==nil pc+=b 165 | OP_JMPUW = 0x29, //!< S unwind_and_jump_to(a) 166 | OP_EXCEPT = 0x2A, //!< B R[a] = exc 167 | OP_RESCUE = 0x2B, //!< BB R[b] = R[a].isa?(R[b]) 168 | OP_RAISEIF = 0x2C, //!< B raise(R[a]) if R[a] 169 | OP_SSEND = 0x2D, //!< BBB R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) 170 | OP_SSENDB = 0x2E, //!< BBB R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) 171 | OP_SEND = 0x2F, //!< BBB R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) 172 | OP_SENDB = 0x30, //!< BBB R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) 173 | OP_CALL = 0x31, //!< Z R[0] = self.call(frame.argc, frame.argv) 174 | OP_SUPER = 0x32, //!< BB R[a] = super(R[a+1],... ,R[a+b+1]) 175 | OP_ARGARY = 0x33, //!< BS R[a] = argument array (16=m5:r1:m5:d1:lv4) 176 | OP_ENTER = 0x34, //!< W arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) 177 | OP_KEY_P = 0x35, //!< BB R[a] = kdict.key?(Syms[b]) 178 | OP_KEYEND = 0x36, //!< Z raise unless kdict.empty? 179 | OP_KARG = 0x37, //!< BB R[a] = kdict[Syms[b]]; kdict.delete(Syms[b]) 180 | OP_RETURN = 0x38, //!< B return R[a] (normal) 181 | OP_RETURN_BLK = 0x39, //!< B return R[a] (in-block return) 182 | OP_BREAK = 0x3A, //!< B break R[a] 183 | OP_BLKPUSH = 0x3B, //!< BS R[a] = block (16=m5:r1:m5:d1:lv4) 184 | OP_ADD = 0x3C, //!< B R[a] = R[a]+R[a+1] 185 | OP_ADDI = 0x3D, //!< BB R[a] = R[a]+mrb_int(b) 186 | OP_SUB = 0x3E, //!< B R[a] = R[a]-R[a+1] 187 | OP_SUBI = 0x3F, //!< BB R[a] = R[a]-mrb_int(b) 188 | OP_MUL = 0x40, //!< B R[a] = R[a]*R[a+1] 189 | OP_DIV = 0x41, //!< B R[a] = R[a]/R[a+1] 190 | OP_EQ = 0x42, //!< B R[a] = R[a]==R[a+1] 191 | OP_LT = 0x43, //!< B R[a] = R[a]R[a+1] 194 | OP_GE = 0x46, //!< B R[a] = R[a]>=R[a+1] 195 | OP_ARRAY = 0x47, //!< BB R[a] = ary_new(R[a],R[a+1]..R[a+b]) 196 | OP_ARRAY2 = 0x48, //!< BBB R[a] = ary_new(R[b],R[b+1]..R[b+c]) 197 | OP_ARYCAT = 0x49, //!< B ary_cat(R[a],R[a+1]) 198 | OP_ARYPUSH = 0x4A, //!< BB ary_push(R[a],R[a+1]..R[a+b]) 199 | OP_ARYDUP = 0x4B, //!< B R[a] = ary_dup(R[a]) 200 | OP_AREF = 0x4C, //!< BBB R[a] = R[b][c] 201 | OP_ASET = 0x4D, //!< BBB R[b][c] = R[a] 202 | OP_APOST = 0x4E, //!< BBB *R[a],R[a+1]..R[a+c] = R[a][b..] 203 | OP_INTERN = 0x4F, //!< B R[a] = intern(R[a]) 204 | OP_SYMBOL = 0x50, //!< BB R[a] = intern(Pool[b]) 205 | OP_STRING = 0x51, //!< BB R[a] = str_dup(Pool[b]) 206 | OP_STRCAT = 0x52, //!< B str_cat(R[a],R[a+1]) 207 | OP_HASH = 0x53, //!< BB R[a] = hash_new(R[a],R[a+1]..R[a+b*2-1]) 208 | OP_HASHADD = 0x54, //!< BB hash_push(R[a],R[a+1]..R[a+b*2]) 209 | OP_HASHCAT = 0x55, //!< B R[a] = hash_cat(R[a],R[a+1]) 210 | OP_LAMBDA = 0x56, //!< BB R[a] = lambda(Irep[b],L_LAMBDA) 211 | OP_BLOCK = 0x57, //!< BB R[a] = lambda(Irep[b],L_BLOCK) 212 | OP_METHOD = 0x58, //!< BB R[a] = lambda(Irep[b],L_METHOD) 213 | OP_RANGE_INC = 0x59, //!< B R[a] = range_new(R[a],R[a+1],FALSE) 214 | OP_RANGE_EXC = 0x5A, //!< B R[a] = range_new(R[a],R[a+1],TRUE) 215 | OP_OCLASS = 0x5B, //!< B R[a] = ::Object 216 | OP_CLASS = 0x5C, //!< BB R[a] = newclass(R[a],Syms[b],R[a+1]) 217 | OP_MODULE = 0x5D, //!< BB R[a] = newmodule(R[a],Syms[b]) 218 | OP_EXEC = 0x5E, //!< BB R[a] = blockexec(R[a],Irep[b]) 219 | OP_DEF = 0x5F, //!< BB R[a].newmethod(Syms[b],R[a+1]); R[a] = Syms[b] 220 | OP_ALIAS = 0x60, //!< BB alias_method(target_class,Syms[a],Syms[b]) 221 | OP_UNDEF = 0x61, //!< B undef_method(target_class,Syms[a]) 222 | OP_SCLASS = 0x62, //!< B R[a] = R[a].singleton_class 223 | OP_TCLASS = 0x63, //!< B R[a] = target_class 224 | OP_DEBUG = 0x64, //!< BBB print a,b,c 225 | OP_ERR = 0x65, //!< B raise(LocalJumpError, Pool[a]) 226 | OP_EXT1 = 0x66, //!< Z make 1st operand (a) 16bit 227 | OP_EXT2 = 0x67, //!< Z make 2nd operand (b) 16bit 228 | OP_EXT3 = 0x68, //!< Z make 1st and 2nd operands 16bit 229 | OP_STOP = 0x69, //!< Z stop VM 230 | }; 231 | 232 | 233 | #ifdef __cplusplus 234 | } 235 | #endif 236 | #endif 237 | -------------------------------------------------------------------------------- /src/regex.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* 6 | * You can use stdlib's malloc() and free() 7 | * CFLAGS=-DREGEX_USE_ALLOC_LIBC make 8 | */ 9 | #ifndef REGEX_USE_ALLOC_LIBC 10 | #define REGEX_ALLOC(size) RegexAllocProc(size) /* override */ 11 | #define REGEX_FREE(ptr) RegexFreeProc(ptr) /* override */ 12 | void *(* RegexAllocProc)(size_t); 13 | void (* RegexFreeProc)(void *); 14 | void RegexSetAllocProcs(void *(*allocProcPtr)(size_t), void (*freeProcPtr)(void *)) 15 | { 16 | RegexAllocProc = allocProcPtr; 17 | RegexFreeProc = freeProcPtr; 18 | } 19 | #else 20 | #include 21 | #define REGEX_ALLOC(size) malloc(size) 22 | #define REGEX_FREE(ptr) free(ptr) 23 | #endif /* REGEX_USE_ALLOC_LIBC */ 24 | 25 | typedef enum { 26 | RE_TYPE_TERM = 0, // sentinel of finishing expression. It must be 0 27 | RE_TYPE_LIT, // literal 28 | RE_TYPE_DOT, // . 29 | RE_TYPE_QUESTION, // ? 30 | RE_TYPE_STAR, // * 31 | RE_TYPE_PLUS, // + 32 | RE_TYPE_BEGIN, // ^ 33 | RE_TYPE_END, // $ 34 | RE_TYPE_BRACKET, // [ ] 35 | RE_TYPE_LPAREN, // ( 36 | RE_TYPE_RPAREN, // ) 37 | } ReType; 38 | 39 | /* 40 | * An element of Regular Expression Tree 41 | */ 42 | typedef struct re_atom { 43 | ReType type; 44 | union { 45 | unsigned char ch; // literal in RE_TYPE_LIT 46 | unsigned char *ccl; // pointer to content in [ ] RE_TYPE_BRACKET 47 | }; 48 | } ReAtom; 49 | 50 | typedef struct re_state { 51 | char *original_text_top_addr; 52 | int current_re_nsub; 53 | int max_re_nsub; 54 | signed char *match_index_data; 55 | } ReState; 56 | 57 | static int match(ReState *rs, ReAtom *regexp, const char *text); 58 | static int matchstar(ReState *rs, ReAtom *c, ReAtom *regexp, const char *text); 59 | static int matchhere(ReState *rs, ReAtom *regexp, const char *text); 60 | static int matchone(ReState *rs, ReAtom *p, const char *text); 61 | static int matchquestion(ReState *rs, ReAtom *regexp, const char *text); 62 | static int matchchars(ReState *rs, const unsigned char *s, const char *text); 63 | static int matchbetween(ReState *rs, const unsigned char *s, const char *text); 64 | 65 | /* 66 | * report nsub 67 | */ 68 | static void 69 | re_report_nsub(ReState *rs, const char *text) 70 | { 71 | int pos = (int)((long)text - (long)rs->original_text_top_addr); 72 | rs->match_index_data[pos] = rs->current_re_nsub; 73 | } 74 | #define REPORT_WITHOUT_RETURN (re_report_nsub(rs, text)) 75 | #define REPORT \ 76 | do { \ 77 | re_report_nsub(rs, text); \ 78 | return 1; \ 79 | } while (0) 80 | /* 81 | * matcher functions 82 | */ 83 | static int 84 | matchone(ReState *rs, ReAtom *p, const char *text) 85 | { 86 | if ((p->type == RE_TYPE_LIT && p->ch == text[0]) || (p->type == RE_TYPE_DOT)) 87 | REPORT; 88 | if (p->type == RE_TYPE_BRACKET) return matchchars(rs, p->ccl, text); 89 | return 0; 90 | } 91 | 92 | static int 93 | matchquestion(ReState *rs, ReAtom *regexp, const char *text) 94 | { 95 | if ((matchone(rs, regexp, text) && matchhere(rs, (regexp + 2), text + 1)) 96 | || matchhere(rs, (regexp + 2), text)) { 97 | return 1; // do not REPORT 98 | } else { 99 | return 0; 100 | } 101 | } 102 | 103 | /* matchhere: search for regexp at beginning of text */ 104 | static int 105 | matchhere(ReState *rs, ReAtom *regexp, const char *text) 106 | { 107 | do { 108 | if (regexp->type == RE_TYPE_TERM) 109 | return 1; // do not REPORT; 110 | if (regexp->type == RE_TYPE_LPAREN) { 111 | rs->max_re_nsub++; 112 | rs->current_re_nsub = rs->max_re_nsub; 113 | return matchhere(rs, (regexp + 1), text); 114 | } 115 | if (regexp->type == RE_TYPE_RPAREN) { 116 | rs->current_re_nsub = 0; /* Nested paren doesn't work. eg) `ab(c(de))fg` */ 117 | return matchhere(rs, (regexp + 1), text); 118 | } 119 | if ((regexp + 1)->type == RE_TYPE_QUESTION) 120 | return matchquestion(rs, regexp, text); 121 | if ((regexp + 1)->type == RE_TYPE_STAR) 122 | return matchstar(rs, regexp, (regexp + 2), text); 123 | if ((regexp + 1)->type == RE_TYPE_PLUS) 124 | return matchone(rs, regexp, text) && matchstar(rs, regexp, (regexp + 2), text + 1); 125 | if (regexp->type == RE_TYPE_END && (regexp + 1)->type == RE_TYPE_TERM) 126 | return text[0] == '\0'; 127 | if (text[0] != '\0' && (regexp->type == RE_TYPE_DOT || (regexp->type == RE_TYPE_LIT && regexp->ch == text[0]))) { 128 | REPORT_WITHOUT_RETURN; 129 | return matchhere(rs, (regexp + 1), text + 1); 130 | } 131 | } while (text[0] != '\0' && matchone(rs, regexp++, text++)); 132 | return 0; 133 | } 134 | 135 | static int 136 | matchstar(ReState *rs, ReAtom *c, ReAtom *regexp, const char *text_) 137 | { 138 | char *text; 139 | /* leftmost && longest */ 140 | for (text = (char *)text_; 141 | text[0] != '\0' && ( 142 | (c->type == RE_TYPE_LIT && text[0] == c->ch) || 143 | (c->type == RE_TYPE_BRACKET && matchchars(rs, c->ccl, text)) || 144 | c->type == RE_TYPE_DOT 145 | ); 146 | text++) 147 | REPORT_WITHOUT_RETURN; 148 | do { /* * matches zero or more */ 149 | if (matchhere(rs, regexp, text)) 150 | return 1; //REPORT; 151 | } while (text-- > text_); 152 | return 0; 153 | } 154 | 155 | static int 156 | matchbetween(ReState *rs, const unsigned char* s, const char *text) 157 | { 158 | if ((text[0] != '-') && (s[0] != '\0') && (s[0] != '-') && 159 | (s[1] == '-') && (s[1] != '\0') && 160 | (s[2] != '\0') && ((text[0] >= s[0]) && (text[0] <= s[2]))) { 161 | REPORT; 162 | } else { 163 | return 0; 164 | } 165 | } 166 | 167 | static int 168 | matchchars(ReState *rs, const unsigned char* s, const char *text) 169 | { 170 | do { 171 | if (matchbetween(rs, s, text)) { 172 | REPORT; 173 | } else if (s[0] == '\\') { 174 | s += 1; 175 | if (text[0] == s[0]) { 176 | REPORT; 177 | } 178 | } else if (text[0] == s[0]) { 179 | if (text[0] == '-') { 180 | return (s[-1] == '\0') || (s[1] == '\0'); 181 | } else { 182 | REPORT; 183 | } 184 | } 185 | } while (*s++ != '\0'); 186 | return 0; 187 | } 188 | 189 | static int 190 | match(ReState *rs, ReAtom *regexp, const char *text) 191 | { 192 | if (regexp->type == RE_TYPE_BEGIN) 193 | return (matchhere(rs, (regexp + 1), text)); 194 | do { /* must look even if string is empty */ 195 | if (matchhere(rs, regexp, text)) { 196 | return 1; 197 | } else { 198 | /* reset match_index_data */ 199 | memset(rs->match_index_data, -1, strlen(text)); 200 | rs->current_re_nsub = 0; 201 | rs->max_re_nsub = 0; 202 | } 203 | } while (*text++ != '\0'); 204 | return 0; 205 | } 206 | 207 | void 208 | set_match_data(ReState *rs, size_t nmatch, regmatch_t *pmatch, size_t len) 209 | { 210 | int i; 211 | for (i = 0; i < nmatch; i++) { 212 | (pmatch + i)->rm_so = -1; 213 | (pmatch + i)->rm_eo = -1; 214 | } 215 | bool scanning = false; 216 | for (i = len - 1; i > -1; i--) { 217 | if (rs->match_index_data[i] < 0) { 218 | if (scanning) break; 219 | continue; 220 | } else { 221 | scanning = true; 222 | } 223 | if (pmatch->rm_eo < 0) 224 | pmatch->rm_eo = i + 1; 225 | if ( (pmatch + rs->match_index_data[i])->rm_eo < 0 ) 226 | (pmatch + rs->match_index_data[i])->rm_eo = i + 1; 227 | (pmatch + rs->match_index_data[i])->rm_so = i; 228 | } 229 | pmatch[0].rm_so = i + 1; 230 | } 231 | 232 | /* 233 | * public functions 234 | */ 235 | int 236 | regexec(regex_t *preg, const char *text, size_t nmatch, regmatch_t *pmatch, int _eflags) 237 | { 238 | ReState rs; 239 | rs.original_text_top_addr = (void *)text; 240 | size_t len = strlen(text); 241 | signed char mid[len]; 242 | rs.match_index_data = mid; 243 | memset(rs.match_index_data, -1, len); 244 | rs.current_re_nsub = 0; 245 | rs.max_re_nsub = 0; 246 | if (match(&rs, preg->atoms, text)) { 247 | set_match_data(&rs, nmatch, pmatch, len); 248 | return 0; /* success */ 249 | } else { 250 | return -1; /* to be correct, it should be a thing like REG_NOMATCH */ 251 | } 252 | } 253 | 254 | size_t 255 | gen_ccl(ReAtom *atom, unsigned char **ccl, const char *snippet, size_t len, bool dry_run) 256 | { 257 | if (len == 0) len = strlen(snippet); 258 | if (!dry_run) { 259 | memcpy(*ccl, snippet, len); 260 | (*ccl)[len] = '\0'; 261 | atom->ccl = *ccl; 262 | atom->type = RE_TYPE_BRACKET; 263 | *ccl += len + 1; 264 | } 265 | return len + 1; 266 | } 267 | #define gen_ccl_const(atom, ccl, snippet, dry_run) gen_ccl(atom, ccl, snippet, 0, dry_run) 268 | 269 | /* 270 | * compile regular expression pattern 271 | * _cflags is dummy 272 | */ 273 | #define REGEX_DEF_w "a-zA-Z0-9_" 274 | #define REGEX_DEF_s " \t\f\r\n" 275 | #define REGEX_DEF_d "0-9" 276 | int 277 | regcomp(regex_t *preg, const char *pattern, int _cflags) 278 | { 279 | ReAtom *atoms = REGEX_ALLOC(sizeof(ReAtom)); 280 | preg->re_nsub = 0; 281 | size_t ccl_len = 0; // total length of ccl(s) 282 | size_t len; 283 | bool dry_run = true; 284 | char *pattern_index = (char *)pattern; 285 | uint16_t atoms_count = 1; 286 | unsigned char *ccl = '\0'; 287 | /* 288 | * Just calculates size of atoms as a dry-run, 289 | * then makes atoms and ccl(s) at the second time 290 | */ 291 | for (;;) { 292 | while (pattern_index[0] != '\0') { 293 | switch (pattern_index[0]) { 294 | case '.': 295 | atoms->type = RE_TYPE_DOT; 296 | break; 297 | case '?': 298 | atoms->type = RE_TYPE_QUESTION; 299 | break; 300 | case '*': 301 | atoms->type = RE_TYPE_STAR; 302 | break; 303 | case '+': 304 | atoms->type = RE_TYPE_PLUS; 305 | break; 306 | case '^': 307 | atoms->type = RE_TYPE_BEGIN; 308 | break; 309 | case '$': 310 | atoms->type = RE_TYPE_END; 311 | break; 312 | case '(': 313 | atoms->type = RE_TYPE_LPAREN; 314 | if (!dry_run) preg->re_nsub++; 315 | break; 316 | case ')': 317 | atoms->type = RE_TYPE_RPAREN; 318 | break; 319 | case '\\': 320 | switch (pattern_index[1]) { 321 | case '\0': 322 | atoms->type = RE_TYPE_LIT; 323 | atoms->ch = '\\'; 324 | break; 325 | case 'w': 326 | pattern_index++; 327 | ccl_len += gen_ccl_const(atoms, &ccl, REGEX_DEF_w, dry_run); 328 | break; 329 | case 's': 330 | pattern_index++; 331 | ccl_len += gen_ccl_const(atoms, &ccl, REGEX_DEF_s, dry_run); 332 | break; 333 | case 'd': 334 | pattern_index++; 335 | ccl_len += gen_ccl_const(atoms, &ccl, REGEX_DEF_d, dry_run); 336 | break; 337 | default: 338 | pattern_index++; 339 | atoms->type = RE_TYPE_LIT; 340 | atoms->ch = pattern_index[0]; 341 | } 342 | break; 343 | case '[': 344 | pattern_index++; 345 | /* 346 | * pattern [] must contain at least one letter. 347 | * first letter of the content should be ']' if you want to match literal ']' 348 | */ 349 | for (len = 1; 350 | pattern_index[len] != '\0' && (pattern_index[len] != ']'); 351 | len++) 352 | ; 353 | ccl_len += gen_ccl(atoms, &ccl, pattern_index, len, dry_run); 354 | pattern_index += len; 355 | break; 356 | default: 357 | atoms->type = RE_TYPE_LIT; 358 | atoms->ch = pattern_index[0]; 359 | break; 360 | } 361 | pattern_index++; 362 | if (dry_run) { 363 | atoms_count++; 364 | } else { 365 | atoms++; 366 | } 367 | } 368 | if (dry_run) { 369 | dry_run = false; 370 | pattern_index = (char *)pattern; 371 | REGEX_FREE(atoms); 372 | atoms = (ReAtom *)REGEX_ALLOC(sizeof(ReAtom) * atoms_count + ccl_len); 373 | ccl = (unsigned char *)(atoms + atoms_count); 374 | } else { 375 | atoms->type = RE_TYPE_TERM; 376 | preg->atoms = atoms - atoms_count + 1; 377 | break; 378 | } 379 | } 380 | return 0; 381 | } 382 | 383 | /* 384 | * free regex_t object 385 | */ 386 | void 387 | regfree(regex_t *preg) 388 | { 389 | REGEX_FREE(preg->atoms); 390 | } 391 | 392 | -------------------------------------------------------------------------------- /src/scope.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | void generateCodePool(Scope *self, uint16_t size) 10 | { 11 | CodePool *pool = (CodePool *)picorbc_alloc(sizeof(CodePool) - IREP_HEADER_SIZE + size); 12 | pool->size = size; 13 | pool->index = 0; 14 | pool->next = NULL; 15 | if (self->current_code_pool) 16 | self->current_code_pool->next = pool; 17 | self->current_code_pool = pool; 18 | } 19 | 20 | Scope *Scope_new(Scope *upper, bool lvar_top) 21 | { 22 | Scope *self = picorbc_alloc(sizeof(Scope)); 23 | memset(self, 0, sizeof(Scope)); 24 | self->upper = upper; 25 | self->lvar_top = lvar_top; 26 | if (upper != NULL) { 27 | if (upper->first_lower == NULL) { 28 | upper->first_lower = self; 29 | } else { 30 | Scope *prev = upper->first_lower; 31 | for (int i = 1; i < upper->nlowers; i++) prev = prev->next; 32 | prev->next = self; 33 | } 34 | upper->nlowers++; 35 | } 36 | generateCodePool(self, IREP_HEADER_SIZE); 37 | self->first_code_pool = self->current_code_pool; 38 | self->first_code_pool->index = IREP_HEADER_SIZE; 39 | self->nlocals = 1; 40 | self->sp = 1; 41 | self->rlen = 1; 42 | self->exc_handler = NULL; 43 | return self; 44 | } 45 | 46 | void freeLiteralRcsv(Literal *literal) 47 | { 48 | if (literal == NULL) return; 49 | freeLiteralRcsv(literal->next); 50 | picorbc_free(literal); 51 | } 52 | 53 | void freeGenLiteralRcsv(GenLiteral *gen_literal) 54 | { 55 | if (gen_literal == NULL) return; 56 | freeGenLiteralRcsv(gen_literal->prev); 57 | picorbc_free((void *)gen_literal->value); 58 | picorbc_free(gen_literal); 59 | } 60 | 61 | void freeSymbolRcsv(Symbol *symbol) 62 | { 63 | if (symbol == NULL) return; 64 | freeSymbolRcsv(symbol->next); 65 | picorbc_free(symbol); 66 | } 67 | 68 | void Scope_freeLvar(Lvar *lvar) 69 | { 70 | if (lvar == NULL) return; 71 | Scope_freeLvar(lvar->next); 72 | if (lvar->to_be_free) picorbc_free((void *)lvar->name); 73 | picorbc_free(lvar); 74 | } 75 | 76 | void freeAssignSymbol(AssignSymbol *assign_symbol) 77 | { 78 | AssignSymbol *prev; 79 | while (assign_symbol) { 80 | prev = assign_symbol->prev; 81 | picorbc_free((void *)assign_symbol->value); 82 | picorbc_free(assign_symbol); 83 | assign_symbol = prev; 84 | } 85 | } 86 | 87 | void Scope_free(Scope *self) 88 | { 89 | if (self == NULL) return; 90 | Scope_free(self->next); 91 | Scope_free(self->first_lower); 92 | freeLiteralRcsv(self->literal); 93 | freeGenLiteralRcsv(self->gen_literal); 94 | freeSymbolRcsv(self->symbol); 95 | Scope_freeLvar(self->lvar); 96 | freeAssignSymbol(self->last_assign_symbol); 97 | if (self->upper == NULL) { 98 | picorbc_free(self->vm_code); 99 | } 100 | picorbc_free(self); 101 | } 102 | 103 | void Scope_pushNCode_self(Scope *self, uint8_t *str, int size) 104 | { 105 | CodePool *pool; 106 | if (size > CODE_POOL_SIZE) 107 | generateCodePool(self, size); 108 | if (self->current_code_pool->index + size > self->current_code_pool->size) 109 | generateCodePool(self, CODE_POOL_SIZE); 110 | pool = self->current_code_pool; 111 | memcpy(&pool->data[pool->index], str, size); 112 | pool->index += size; 113 | self->vm_code_size = self->vm_code_size + size; 114 | } 115 | 116 | 117 | void Scope_pushCode_self(Scope *self, int val) 118 | { 119 | uint8_t str[1]; 120 | str[0] = (uint8_t)val; 121 | Scope_pushNCode_self(self, str, 1); 122 | } 123 | 124 | static Literal *literal_new(const char *value, LiteralType type) 125 | { 126 | Literal *literal = picorbc_alloc(sizeof(Literal)); 127 | literal->next = NULL; 128 | literal->type = type; 129 | literal->value = value; 130 | return literal; 131 | } 132 | 133 | /* 134 | * returns -1 if literal was not found 135 | */ 136 | int literal_findIndex(Literal *literal, const char *value, LiteralType type) 137 | { 138 | int i = 0; 139 | while (literal != NULL) { 140 | if (literal->type == type && strcmp(literal->value, value) == 0) { 141 | return i; 142 | } 143 | literal = literal->next; 144 | i++; 145 | } 146 | return -1; 147 | } 148 | 149 | int Scope_newLit(Scope *self, const char *value, LiteralType type){ 150 | int index = literal_findIndex(self->literal, value, type); 151 | if (index >= 0) return index; 152 | Literal *newLit = literal_new(value, type); 153 | self->plen++; 154 | Literal *lit = self->literal; 155 | if (lit == NULL) { 156 | self->literal = newLit; 157 | return 0; 158 | } 159 | for (index = 1; ; index++) { 160 | if (lit->next == NULL) break; 161 | lit = lit->next; 162 | } 163 | lit->next = newLit; 164 | return index; 165 | } 166 | 167 | static Symbol *symbol_new(const char *value) 168 | { 169 | Symbol *symbol = picorbc_alloc(sizeof(Symbol)); 170 | symbol->next = NULL; 171 | symbol->value = value; 172 | return symbol; 173 | } 174 | 175 | static Lvar *lvar_new(const char *name, int regnum) 176 | { 177 | Lvar *lvar = picorbc_alloc(sizeof(Lvar)); 178 | lvar->regnum = regnum; 179 | lvar->next = NULL; 180 | lvar->name = name; 181 | lvar->len = strlen(name); 182 | lvar->to_be_free = false; 183 | return lvar; 184 | } 185 | 186 | /* 187 | * returns -1 if symbol was not found 188 | */ 189 | int symbol_findIndex(Symbol *symbol, const char *value) 190 | { 191 | int i = 0; 192 | while (symbol != NULL) { 193 | if (strcmp(symbol->value, value) == 0) { 194 | return i; 195 | } 196 | symbol = symbol->next; 197 | i++; 198 | } 199 | return -1; 200 | } 201 | 202 | int Scope_newSym(Scope *self, const char *value){ 203 | int index = symbol_findIndex(self->symbol, value); 204 | if (index >= 0) return index; 205 | Symbol *newSym = symbol_new(value); 206 | self->slen++; 207 | Symbol *sym = self->symbol; 208 | if (sym == NULL) { 209 | self->symbol = newSym; 210 | return 0; 211 | } 212 | for (index = 1; ; index++) { 213 | if (sym->next == NULL) break; 214 | sym = sym->next; 215 | } 216 | sym->next = newSym; 217 | return index; 218 | } 219 | 220 | int Scope_assignSymIndex(Scope *self, const char *method_name) 221 | { 222 | size_t length = strlen(method_name); 223 | char *assign_method_name = picorbc_alloc(length + 2); 224 | memcpy(assign_method_name, method_name, length); 225 | assign_method_name[length] = '='; 226 | assign_method_name[length + 1] = '\0'; 227 | int symIndex = symbol_findIndex(self->symbol, assign_method_name); 228 | if (symIndex < 0) { 229 | symIndex = Scope_newSym(self, (const char *)assign_method_name); 230 | AssignSymbol *assign_symbol = picorbc_alloc(sizeof(AssignSymbol)); 231 | assign_symbol->prev = NULL; 232 | assign_symbol->value = assign_method_name; 233 | assign_symbol->prev = self->last_assign_symbol; 234 | self->last_assign_symbol = assign_symbol; 235 | } else { 236 | picorbc_free(assign_method_name); 237 | } 238 | return symIndex; 239 | } 240 | 241 | /* 242 | * reg_num = 0 if lvar was not found 243 | */ 244 | LvarScopeReg Scope_lvar_findRegnum(Scope *self, const char *name) 245 | { 246 | LvarScopeReg scopeReg = {0, 0}; 247 | Scope *scope = self; 248 | Lvar *lvar; 249 | while (1) { 250 | lvar = scope->lvar; 251 | while (lvar != NULL) { 252 | if (strcmp(lvar->name, name) == 0) { 253 | scopeReg.reg_num = lvar->regnum; 254 | return scopeReg; 255 | } 256 | lvar = lvar->next; 257 | } 258 | if (scope->upper == NULL || scope->lvar_top == true) break; 259 | scopeReg.scope_num++; 260 | scope = scope->upper; 261 | }; 262 | return (LvarScopeReg){0, 0}; 263 | } 264 | 265 | void 266 | Scope_newLvar(Scope *self, const char *name, int newRegnum){ 267 | Lvar *newLvar = lvar_new(name, newRegnum); 268 | self->nlocals++; 269 | if (self->lvar == NULL) { 270 | self->lvar = newLvar; 271 | } else { 272 | Lvar *lvar = self->lvar; 273 | while (lvar->next) { 274 | lvar = lvar->next; 275 | } 276 | lvar->next = newLvar; 277 | } 278 | } 279 | 280 | void Scope_setSp(Scope *self, uint16_t sp){ 281 | self->sp = sp; 282 | if (self->rlen < self->sp) self->rlen = self->sp; 283 | } 284 | 285 | void Scope_push(Scope *self){ 286 | self->sp++; 287 | if (self->rlen < self->sp) self->rlen = self->sp; 288 | } 289 | 290 | int scope_codeSize(CodePool *code_pool) 291 | { 292 | int size = 0; 293 | while (code_pool != NULL) { 294 | size += code_pool->index; 295 | code_pool = code_pool->next; 296 | } 297 | return size; 298 | } 299 | 300 | #define PICORUBYNULL "PICORUBYNULL\xF5" 301 | /* 302 | * Replace back to null letter 303 | * -> see REPLACE_NULL_PICORUBY, too 304 | */ 305 | size_t replace_picoruby_null(char *value) 306 | { 307 | int i = 0; 308 | int j = 0; 309 | char j_value[strlen(value) + 1]; 310 | while (value[i]) { 311 | if (value[i] != '\xF5') { 312 | j_value[j] = value[i]; 313 | } else if (strncmp(value+i+1, PICORUBYNULL, sizeof(PICORUBYNULL)-1) == 0) { 314 | j_value[j] = '\0'; 315 | i += sizeof(PICORUBYNULL) - 1; 316 | } 317 | i++; 318 | j++; 319 | } 320 | j_value[j] = '\0'; 321 | if (j < i) memcpy(value, j_value, j); 322 | return j; 323 | } 324 | 325 | void Scope_finish(Scope *scope) 326 | { 327 | scope->ilen = (uint32_t)scope->vm_code_size; 328 | ExcHandler *tmp; 329 | ExcHandler *exc_handler = scope->exc_handler; 330 | for (int i = 0; i < scope->clen; i++) { 331 | Scope_pushNCode_self(scope, exc_handler->table, 13); 332 | tmp = exc_handler; 333 | exc_handler = exc_handler->next; 334 | picorbc_free(tmp); 335 | } 336 | int len; 337 | uint8_t *data = scope->first_code_pool->data; 338 | // literal 339 | Literal *lit; 340 | Scope_pushCode((scope->plen >> 8) & 0xff); 341 | Scope_pushCode(scope->plen & 0xff); 342 | lit = scope->literal; 343 | while (lit != NULL) { 344 | Scope_pushCode(lit->type); 345 | if (lit->type == FLOAT_LITERAL) { 346 | double d = atof(lit->value); 347 | Scope_pushNCode((uint8_t *)&d, 8); 348 | } else { 349 | len = replace_picoruby_null((char *)lit->value); 350 | lit->type = len<<2; /* used in cdump_pool() */ 351 | Scope_pushCode((len >> 8) & 0xff); 352 | Scope_pushCode(len & 0xff); 353 | Scope_pushNCode((uint8_t *)lit->value, len); 354 | Scope_pushCode(0); 355 | } 356 | lit = lit->next; 357 | } 358 | // symbol 359 | Symbol *sym; 360 | Scope_pushCode((scope->slen >> 8) & 0xff); 361 | Scope_pushCode(scope->slen & 0xff); 362 | sym = scope->symbol; 363 | while (sym != NULL) { 364 | /* len is used in dump.c */ 365 | sym->len = replace_picoruby_null((char *)sym->value); 366 | Scope_pushCode((sym->len >>8) & 0xff); 367 | Scope_pushCode(sym->len & 0xff); 368 | Scope_pushNCode((uint8_t *)sym->value, sym->len); 369 | Scope_pushCode(0); 370 | sym = sym->next; 371 | } 372 | // irep header - record length. 373 | { 374 | scope->vm_code_size += IREP_HEADER_SIZE; 375 | data[0] = ((scope->vm_code_size >> 24) & 0xff); 376 | data[1] = ((scope->vm_code_size >> 16) & 0xff); 377 | data[2] = ((scope->vm_code_size >> 8) & 0xff); 378 | data[3] = (scope->vm_code_size & 0xff); 379 | } 380 | // nlocals 381 | data[4] = (scope->nlocals >> 8) & 0xff; 382 | data[5] = scope->nlocals & 0xff; 383 | // nregs 384 | scope->rlen++; 385 | data[6] = ((scope->rlen) >> 8) & 0xff; 386 | data[7] = (scope->rlen) & 0xff; 387 | // rlen (Children) 388 | data[8] = (scope->nlowers >> 8) & 0xff; 389 | data[9] = scope->nlowers & 0xff; 390 | // clen (Catch handlers) 391 | data[10] = (scope->clen >> 8) & 0xff; 392 | data[11] = scope->clen & 0xff; 393 | // ilen (IREP length) 394 | data[12] = (scope->ilen >> 24) & 0xff; 395 | data[13] = (scope->ilen >> 16) & 0xff; 396 | data[14] = (scope->ilen >> 8) & 0xff; 397 | data[15] = scope->ilen & 0xff; 398 | } 399 | 400 | void freeCodePool(CodePool *pool) 401 | { 402 | CodePool *next ; 403 | while (1) { 404 | next = pool->next; 405 | picorbc_free(pool); 406 | if (next == NULL) break; 407 | pool = next; 408 | } 409 | } 410 | 411 | void Scope_freeCodePool(Scope *self) 412 | { 413 | if (self == NULL) return; 414 | Scope_freeCodePool(self->next); 415 | Scope_freeCodePool(self->first_lower); 416 | freeCodePool(self->first_code_pool); 417 | } 418 | 419 | int Scope_updateVmCodeSizeThenReturnTotalSize(Scope *self) 420 | { 421 | int totalSize = 0; 422 | if (self == NULL) return 0; 423 | /* check if it's an empty child scope like `class A; #HERE; end` */ 424 | if (self->first_code_pool->next == NULL && 425 | self->first_code_pool->index == IREP_HEADER_SIZE) return 0; 426 | totalSize += Scope_updateVmCodeSizeThenReturnTotalSize(self->first_lower); 427 | totalSize += Scope_updateVmCodeSizeThenReturnTotalSize(self->next); 428 | self->vm_code_size = scope_codeSize(self->first_code_pool); 429 | totalSize += self->vm_code_size; 430 | return totalSize; 431 | } 432 | 433 | -------------------------------------------------------------------------------- /src/dump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #define PICORB_ASPEC_REQ(a) (((a) >> 18) & 0x1f) 12 | #define PICORB_ASPEC_OPT(a) (((a) >> 13) & 0x1f) 13 | #define PICORB_ASPEC_REST(a) (((a) >> 12) & 0x1) 14 | #define PICORB_ASPEC_POST(a) (((a) >> 7) & 0x1f) 15 | #define PICORB_ASPEC_KEY(a) (((a) >> 2) & 0x1f) 16 | #define PICORB_ASPEC_KDICT(a) (((a) >> 1) & 0x1) 17 | #define PICORB_ASPEC_BLOCK(a) ((a) & 1) 18 | 19 | #define PICOPEEK_B(irep) (*(irep)) 20 | 21 | #define PICOPEEK_S(irep) ((irep)[0]<<8|(irep)[1]) 22 | #define PICOPEEK_W(irep) ((irep)[0]<<16|(irep)[1]<<8|(irep)[2]) 23 | 24 | #define PICOREAD_B() PICOPEEK_B(irep++) 25 | #define PICOREAD_S() (irep+=2, PICOPEEK_S(irep-2)) 26 | #define PICOREAD_W() (irep+=3, PICOPEEK_W(irep-3)) 27 | 28 | #define PICOFETCH_Z() /* nothing */ 29 | #define PICOFETCH_B() do {a=PICOREAD_B();} while (0) 30 | #define PICOFETCH_BB() do {a=PICOREAD_B(); b=PICOREAD_B();} while (0) 31 | #define PICOFETCH_BBB() do {a=PICOREAD_B(); b=PICOREAD_B(); c=PICOREAD_B();} while (0) 32 | #define PICOFETCH_BS() do {a=PICOREAD_B(); b=PICOREAD_S();} while (0) 33 | #define PICOFETCH_BSS() do {a=PICOREAD_B(); b=PICOREAD_S(); c=PICOREAD_S();} while (0) 34 | #define PICOFETCH_S() do {a=PICOREAD_S();} while (0) 35 | #define PICOFETCH_W() do {a=PICOREAD_W();} while (0) 36 | 37 | const char C_FORMAT_LINES[5][22] = { 38 | "#include ", 39 | "#ifdef __cplusplus", 40 | "extern const uint8_t ", 41 | "#endif", 42 | "const uint8_t ", 43 | }; 44 | 45 | void 46 | print_lv_a(void *dummy, uint8_t *irep, uint16_t a) 47 | { 48 | } 49 | void 50 | print_lv_ab(void *dummy, uint8_t *irep, uint16_t a, uint16_t b) 51 | { 52 | } 53 | 54 | char* 55 | pico_mrb_sym_dump(char *s, uint16_t b) 56 | { 57 | sprintf(s, "%d", b); 58 | return s; 59 | } 60 | 61 | #define CASE(insn,ops) case insn: PICOFETCH_ ## ops (); 62 | 63 | void 64 | Dump_hexDump(FILE *fp, uint8_t *irep) 65 | { 66 | char mrb[10]; 67 | uint32_t len = 0; 68 | len += *(irep + 12) << 24; 69 | len += *(irep + 13) << 16; 70 | len += *(irep + 14) << 8; 71 | len += *(irep + 15); 72 | fprintf(fp, "pos: %d / len: %"PRId32"\n", *irep, len); 73 | 74 | irep += 16; /* skip irep header */ 75 | 76 | uint8_t *opstart = irep; 77 | 78 | uint8_t *irepend = irep + len; 79 | 80 | int i = 0; /*dummy*/ 81 | 82 | while (irep < irepend) { 83 | fprintf(fp, " 1 %03d ", (int)(irep - opstart)); 84 | uint32_t a; 85 | uint16_t b = 0; 86 | uint16_t c; 87 | uint8_t ins = *irep++; 88 | switch (ins) { 89 | CASE(OP_NOP, Z); 90 | fprintf(fp, "OP_NOP\n"); 91 | return; 92 | break; 93 | CASE(OP_MOVE, BB); 94 | fprintf(fp, "OP_MOVE\tR%"PRId32"\tR%d\t", a, b); 95 | print_lv_ab(mrb, irep, a, b); 96 | break; 97 | // CASE(OP_LOADL16, BS); 98 | // goto op_loadl; 99 | 100 | CASE(OP_LOADL, BB); 101 | op_loadl: 102 | // switch (irep->pool[b].tt) { 103 | // case IREP_TT_FLOAT: 104 | //#ifndef PICORB_NO_FLOAT 105 | // fprintf(fp, "OP_LOADL\tR%ld\tL(%d)\t; %f", a, b, (double)irep->pool[b].u.f); 106 | //#endif 107 | // break; 108 | // case IREP_TT_INT32: 109 | // fprintf(fp, "OP_LOADL\tR%ld\tL(%d)\t; %" PRId32, a, b, irep->pool[b].u.i32); 110 | // break; 111 | //#ifdef PICORB_64BIT 112 | // case IREP_TT_INT64: 113 | // fprintf(fp, "OP_LOADL\tR%ld\tL(%d)\t; %" PRId64, a, b, irep->pool[b].u.i64); 114 | // break; 115 | //#endif 116 | // default: 117 | fprintf(fp, "OP_LOADL\tR%"PRId32"\tL(%d)\t", a, b); 118 | break; 119 | // } 120 | print_lv_a(mrb, irep, a); 121 | break; 122 | CASE(OP_LOADI, BB); 123 | fprintf(fp, "OP_LOADI\tR%"PRId32"\t%d\t", a, b); 124 | print_lv_a(mrb, irep, a); 125 | break; 126 | CASE(OP_LOADINEG, BB); 127 | fprintf(fp, "OP_LOADI\tR%"PRId32"\t-%d\t", a, b); 128 | print_lv_a(mrb, irep, a); 129 | break; 130 | CASE(OP_LOADI16, BS); 131 | fprintf(fp, "OP_LOADI16\tR%"PRId32"\t%d\t", a, (int)(int16_t)b); 132 | print_lv_a(mrb, irep, a); 133 | break; 134 | CASE(OP_LOADI32, BSS); 135 | fprintf(fp, "OP_LOADI32\tR%"PRId32"\t%d\t", a, (int32_t)(((uint32_t)b<<16)+c)); 136 | print_lv_a(mrb, irep, a); 137 | break; 138 | CASE(OP_LOADI__1, B); 139 | fprintf(fp, "OP_LOADI__1\tR%"PRId32"\t\t", a); 140 | print_lv_a(mrb, irep, a); 141 | break; 142 | CASE(OP_LOADI_0, B); goto L_LOADI; 143 | CASE(OP_LOADI_1, B); goto L_LOADI; 144 | CASE(OP_LOADI_2, B); goto L_LOADI; 145 | CASE(OP_LOADI_3, B); goto L_LOADI; 146 | CASE(OP_LOADI_4, B); goto L_LOADI; 147 | CASE(OP_LOADI_5, B); goto L_LOADI; 148 | CASE(OP_LOADI_6, B); goto L_LOADI; 149 | CASE(OP_LOADI_7, B); 150 | L_LOADI: 151 | fprintf(fp, "OP_LOADI_%d\tR%"PRId32"\t\t", ins-(int)OP_LOADI_0, a); 152 | print_lv_a(mrb, irep, a); 153 | break; 154 | // CASE(OP_LOADSYM16, BS); 155 | // goto op_loadsym; 156 | CASE(OP_LOADSYM, BB); 157 | // op_loadsym: 158 | fprintf(fp, "OP_LOADSYM\tR%"PRId32"\t:%s\t", a, pico_mrb_sym_dump(mrb, b)); 159 | print_lv_a(mrb, irep, a); 160 | break; 161 | CASE(OP_LOADNIL, B); 162 | fprintf(fp, "OP_LOADNIL\tR%"PRId32"\t\t", a); 163 | print_lv_a(mrb, irep, a); 164 | break; 165 | CASE(OP_LOADSELF, B); 166 | fprintf(fp, "OP_LOADSELF\tR%"PRId32"\t\t", a); 167 | print_lv_a(mrb, irep, a); 168 | break; 169 | CASE(OP_LOADT, B); 170 | fprintf(fp, "OP_LOADT\tR%"PRId32"\t\t", a); 171 | print_lv_a(mrb, irep, a); 172 | break; 173 | CASE(OP_LOADF, B); 174 | fprintf(fp, "OP_LOADF\tR%"PRId32"\t\t", a); 175 | print_lv_a(mrb, irep, a); 176 | break; 177 | CASE(OP_GETGV, BB); 178 | fprintf(fp, "OP_GETGV\tR%"PRId32"\t:%s", a, pico_mrb_sym_dump(mrb, b)); 179 | print_lv_a(mrb, irep, a); 180 | break; 181 | CASE(OP_SETGV, BB); 182 | fprintf(fp, "OP_SETGV\t:%s\tR%"PRId32"", pico_mrb_sym_dump(mrb, b), a); 183 | print_lv_a(mrb, irep, a); 184 | break; 185 | CASE(OP_GETSV, BB); 186 | fprintf(fp, "OP_GETSV\tR%"PRId32"\t:%s", a, pico_mrb_sym_dump(mrb, b)); 187 | print_lv_a(mrb, irep, a); 188 | break; 189 | CASE(OP_SETSV, BB); 190 | fprintf(fp, "OP_SETSV\t:%s\tR%"PRId32"", pico_mrb_sym_dump(mrb, b), a); 191 | print_lv_a(mrb, irep, a); 192 | break; 193 | CASE(OP_GETCONST, BB); 194 | fprintf(fp, "OP_GETCONST\tR%"PRId32"\t:%s", a, pico_mrb_sym_dump(mrb, b)); 195 | print_lv_a(mrb, irep, a); 196 | break; 197 | CASE(OP_SETCONST, BB); 198 | fprintf(fp, "OP_SETCONST\t:%s\tR%"PRId32"", pico_mrb_sym_dump(mrb, b), a); 199 | print_lv_a(mrb, irep, a); 200 | break; 201 | CASE(OP_GETMCNST, BB); 202 | fprintf(fp, "OP_GETMCNST\tR%"PRId32"\tR%"PRId32"::%s", a, a, pico_mrb_sym_dump(mrb, b)); 203 | print_lv_a(mrb, irep, a); 204 | break; 205 | CASE(OP_SETMCNST, BB); 206 | fprintf(fp, "OP_SETMCNST\tR%"PRId32"::%s\tR%"PRId32"", a+1, pico_mrb_sym_dump(mrb, b), a); 207 | print_lv_a(mrb, irep, a); 208 | break; 209 | CASE(OP_GETIV, BB); 210 | fprintf(fp, "OP_GETIV\tR%"PRId32"\t%s", a, pico_mrb_sym_dump(mrb, b)); 211 | print_lv_a(mrb, irep, a); 212 | break; 213 | CASE(OP_SETIV, BB); 214 | fprintf(fp, "OP_SETIV\t%s\tR%"PRId32"", pico_mrb_sym_dump(mrb, b), a); 215 | print_lv_a(mrb, irep, a); 216 | break; 217 | CASE(OP_GETUPVAR, BBB); 218 | fprintf(fp, "OP_GETUPVAR\tR%"PRId32"\t%d\t%d", a, b, c); 219 | print_lv_a(mrb, irep, a); 220 | break; 221 | CASE(OP_SETUPVAR, BBB); 222 | fprintf(fp, "OP_SETUPVAR\tR%"PRId32"\t%d\t%d", a, b, c); 223 | print_lv_a(mrb, irep, a); 224 | break; 225 | CASE(OP_GETCV, BB); 226 | fprintf(fp, "OP_GETCV\tR%"PRId32"\t%s", a, pico_mrb_sym_dump(mrb, b)); 227 | print_lv_a(mrb, irep, a); 228 | break; 229 | CASE(OP_SETCV, BB); 230 | fprintf(fp, "OP_SETCV\t%s\tR%"PRId32"", pico_mrb_sym_dump(mrb, b), a); 231 | print_lv_a(mrb, irep, a); 232 | break; 233 | CASE(OP_JMP, S); 234 | i = irep - opstart; 235 | fprintf(fp, "OP_JMP\t%03d", (int)i+(int16_t)a); 236 | break; 237 | CASE(OP_JMPUW, S); 238 | i = irep - opstart; 239 | fprintf(fp, "OP_JMPUW\t%03d", (int)i+(int16_t)a); 240 | break; 241 | CASE(OP_JMPIF, BS); 242 | i = irep - opstart; 243 | fprintf(fp, "OP_JMPIF\tR%"PRId32"\t%03d\t", a, (int)i+(int16_t)b); 244 | print_lv_a(mrb, irep, a); 245 | break; 246 | CASE(OP_JMPNOT, BS); 247 | i = irep - opstart; 248 | fprintf(fp, "OP_JMPNOT\tR%"PRId32"\t%03d\t", a, (int)i+(int16_t)b); 249 | print_lv_a(mrb, irep, a); 250 | break; 251 | CASE(OP_JMPNIL, BS); 252 | i = irep - opstart; 253 | fprintf(fp, "OP_JMPNIL\tR%"PRId32"\t%03d\t", a, (int)i+(int16_t)b); 254 | print_lv_a(mrb, irep, a); 255 | break; 256 | CASE(OP_SSEND, BBB); 257 | fprintf(fp, "OP_SSEND\tR%"PRId32"\t:%s\t%d", a, pico_mrb_sym_dump(mrb, b), c); 258 | break; 259 | CASE(OP_SSENDB, BBB); 260 | fprintf(fp, "OP_SSENDB\tR%"PRId32"\t:%s\t%d", a, pico_mrb_sym_dump(mrb, b), c); 261 | break; 262 | CASE(OP_SEND, BBB); 263 | fprintf(fp, "OP_SEND\tR%"PRId32"\t:%s\t%d", a, pico_mrb_sym_dump(mrb, b), c); 264 | break; 265 | CASE(OP_SENDB, BBB); 266 | fprintf(fp, "OP_SENDB\tR%"PRId32"\t:%s\t%d", a, pico_mrb_sym_dump(mrb, b), c); 267 | break; 268 | CASE(OP_GETIDX, B); 269 | fprintf(fp, "OP_GETIDX\tR%"PRId32"\t(R%"PRId32")", a, a + 1); 270 | break; 271 | CASE(OP_SETIDX, B); 272 | fprintf(fp, "OP_SETIDX\tR%"PRId32"\t(R%"PRId32")\t(R%"PRId32")", a, a + 1, a + 2); 273 | break; 274 | CASE(OP_CALL, Z); 275 | fprintf(fp, "OP_CALL\n"); 276 | break; 277 | CASE(OP_SUPER, BB); 278 | fprintf(fp, "OP_SUPER\tR%"PRId32"\t%d", a, b); 279 | break; 280 | CASE(OP_ARGARY, BS); 281 | fprintf(fp, "OP_ARGARY\tR%"PRId32"\t%d:%d:%d:%d (%d)", a, 282 | (b>>11)&0x3f, 283 | (b>>10)&0x1, 284 | (b>>5)&0x1f, 285 | (b>>4)&0x1, 286 | (b>>0)&0xf); 287 | print_lv_a(mrb, irep, a); 288 | break; 289 | CASE(OP_ENTER, W); 290 | fprintf(fp, "OP_ENTER\t%"PRId32":%"PRId32":%"PRId32":%"PRId32":%"PRId32":%"PRId32":%"PRId32"", 291 | PICORB_ASPEC_REQ(a), 292 | PICORB_ASPEC_OPT(a), 293 | PICORB_ASPEC_REST(a), 294 | PICORB_ASPEC_POST(a), 295 | PICORB_ASPEC_KEY(a), 296 | PICORB_ASPEC_KDICT(a), 297 | PICORB_ASPEC_BLOCK(a)); 298 | break; 299 | CASE(OP_KEY_P, BB); 300 | fprintf(fp, "OP_KEY_P\tR%"PRId32"\t:%s\t", a, pico_mrb_sym_dump(mrb, b)); 301 | print_lv_a(mrb, irep, a); 302 | break; 303 | CASE(OP_KEYEND, Z); 304 | fprintf(fp, "OP_KEYEND"); 305 | break; 306 | CASE(OP_KARG, BB); 307 | fprintf(fp, "OP_KARG\tR%"PRId32"\t:%s\t", a, pico_mrb_sym_dump(mrb, b)); 308 | print_lv_a(mrb, irep, a); 309 | break; 310 | CASE(OP_RETURN, B); 311 | fprintf(fp, "OP_RETURN\tR%"PRId32"\t\t", a); 312 | print_lv_a(mrb, irep, a); 313 | break; 314 | CASE(OP_RETURN_BLK, B); 315 | fprintf(fp, "OP_RETURN_BLK\tR%"PRId32"\t\t", a); 316 | print_lv_a(mrb, irep, a); 317 | break; 318 | CASE(OP_BREAK, B); 319 | fprintf(fp, "OP_BREAK\tR%"PRId32"\t\t", a); 320 | print_lv_a(mrb, irep, a); 321 | break; 322 | CASE(OP_BLKPUSH, BS); 323 | fprintf(fp, "OP_BLKPUSH\tR%"PRId32"\t%d:%d:%d:%d (%d)", a, 324 | (b>>11)&0x3f, 325 | (b>>10)&0x1, 326 | (b>>5)&0x1f, 327 | (b>>4)&0x1, 328 | (b>>0)&0xf); 329 | print_lv_a(mrb, irep, a); 330 | break; 331 | CASE(OP_LAMBDA, BB); 332 | fprintf(fp, "OP_LAMBDA\tR%"PRId32"\tI(%d:%p)", a, b, NULL); 333 | break; 334 | CASE(OP_BLOCK, BB); 335 | fprintf(fp, "OP_BLOCK\tR%"PRId32"\tI(%d:%p)", a, b, NULL); 336 | break; 337 | CASE(OP_METHOD, BB); 338 | fprintf(fp, "OP_METHOD\tR%"PRId32"\tI(%d:%p)", a, b, NULL); 339 | break; 340 | CASE(OP_RANGE_INC, B); 341 | fprintf(fp, "OP_RANGE_INC\tR%"PRId32"\n", a); 342 | break; 343 | CASE(OP_RANGE_EXC, B); 344 | fprintf(fp, "OP_RANGE_EXC\tR%"PRId32"\n", a); 345 | break; 346 | CASE(OP_DEF, BB); 347 | fprintf(fp, "OP_DEF\tR%"PRId32"\t:%s", a, pico_mrb_sym_dump(mrb, b)); 348 | break; 349 | CASE(OP_UNDEF, B); 350 | fprintf(fp, "OP_UNDEF\t:%s", pico_mrb_sym_dump(mrb, b)); 351 | break; 352 | CASE(OP_ALIAS, BB); 353 | fprintf(fp, "OP_ALIAS\t:%s\t%s", pico_mrb_sym_dump(mrb, b), pico_mrb_sym_dump(mrb, b)); 354 | break; 355 | CASE(OP_ADD, B); 356 | fprintf(fp, "OP_ADD\tR%"PRId32"\tR%"PRId32"", a, a+1); 357 | break; 358 | CASE(OP_ADDI, BB); 359 | fprintf(fp, "OP_ADDI\tR%"PRId32"\t%"PRId16"", a, b); 360 | break; 361 | CASE(OP_SUB, B); 362 | fprintf(fp, "OP_SUB\tR%"PRId32"\tR%"PRId32"", a, a+1); 363 | break; 364 | CASE(OP_SUBI, BB); 365 | fprintf(fp, "OP_SUBI\tR%"PRId32"\t%"PRId16"", a, b); 366 | break; 367 | CASE(OP_MUL, B); 368 | fprintf(fp, "OP_MUL\tR%"PRId32"\tR%"PRId32"", a, a+1); 369 | break; 370 | CASE(OP_DIV, B); 371 | fprintf(fp, "OP_DIV\tR%"PRId32"\tR%"PRId32"", a, a+1); 372 | break; 373 | CASE(OP_LT, B); 374 | fprintf(fp, "OP_LT\t\tR%"PRId32"\tR%"PRId32"", a, a+1); 375 | break; 376 | CASE(OP_LE, B); 377 | fprintf(fp, "OP_LE\t\tR%"PRId32"\tR%"PRId32"", a, a+1); 378 | break; 379 | CASE(OP_GT, B); 380 | fprintf(fp, "OP_GT\t\tR%"PRId32"\tR%"PRId32"", a, a+1); 381 | break; 382 | CASE(OP_GE, B); 383 | fprintf(fp, "OP_GE\t\tR%"PRId32"\tR%"PRId32"", a, a+1); 384 | break; 385 | CASE(OP_EQ, B); 386 | fprintf(fp, "OP_EQ\t\tR%"PRId32"\tR%"PRId32"", a, a+1); 387 | break; 388 | CASE(OP_ARRAY, BB); 389 | fprintf(fp, "OP_ARRAY\tR%"PRId32"\t(R%"PRId32")\t%d\t", a, a, b); 390 | print_lv_a(mrb, irep, a); 391 | break; 392 | CASE(OP_ARRAY2, BBB); 393 | fprintf(fp, "OP_ARRAY\tR%"PRId32"\tR%d\t%d\t", a, b, c); 394 | print_lv_ab(mrb, irep, a, b); 395 | break; 396 | CASE(OP_ARYCAT, B); 397 | fprintf(fp, "OP_ARYCAT\tR%"PRId32"\t(R%"PRId32")\t", a, a+1); 398 | print_lv_a(mrb, irep, a); 399 | break; 400 | CASE(OP_ARYPUSH, BB); 401 | fprintf(fp, "OP_ARYPUSH\tR%"PRId32"\t%d\t", a, b); 402 | print_lv_a(mrb, irep, a); 403 | break; 404 | CASE(OP_ARYDUP, B); 405 | fprintf(fp, "OP_ARYDUP\tR%"PRId32"\t", a); 406 | print_lv_a(mrb, irep, a); 407 | break; 408 | CASE(OP_AREF, BBB); 409 | fprintf(fp, "OP_AREF\tR%"PRId32"\tR%d\t%d", a, b, c); 410 | print_lv_ab(mrb, irep, a, b); 411 | break; 412 | CASE(OP_ASET, BBB); 413 | fprintf(fp, "OP_ASET\tR%"PRId32"\tR%d\t%d", a, b, c); 414 | print_lv_ab(mrb, irep, a, b); 415 | break; 416 | CASE(OP_APOST, BBB); 417 | fprintf(fp, "OP_APOST\tR%"PRId32"\t%d\t%d", a, b, c); 418 | print_lv_a(mrb, irep, a); 419 | break; 420 | CASE(OP_INTERN, B); 421 | fprintf(fp, "OP_INTERN\tR%"PRId32"", a); 422 | print_lv_a(mrb, irep, a); 423 | break; 424 | CASE(OP_STRING, BB); 425 | // op_string: 426 | // if ((irep->pool[b].tt & IREP_TT_NFLAG) == 0) { 427 | // fprintf(fp, "OP_STRING\tR%ld\tL(%d)\t; %s", a, b, irep->pool[b].u.str); 428 | // } 429 | // else { 430 | fprintf(fp, "OP_STRING\tR%"PRId32"\tL(%d)\t", a, b); 431 | // } 432 | print_lv_a(mrb, irep, a); 433 | break; 434 | CASE(OP_STRCAT, B); 435 | fprintf(fp, "OP_STRCAT\tR%"PRId32"\t(R%"PRId32")\t", a, a + 1); 436 | print_lv_a(mrb, irep, a); 437 | break; 438 | CASE(OP_HASH, BB); 439 | fprintf(fp, "OP_HASH\tR%"PRId32"\t%d\t", a, b); 440 | print_lv_a(mrb, irep, a); 441 | break; 442 | CASE(OP_HASHADD, BB); 443 | fprintf(fp, "OP_HASHADD\tR%"PRId32"\t%d\t", a, b); 444 | print_lv_a(mrb, irep, a); 445 | break; 446 | CASE(OP_HASHCAT, B); 447 | fprintf(fp, "OP_HASHCAT\tR%"PRId32"\t", a); 448 | print_lv_a(mrb, irep, a); 449 | break; 450 | 451 | CASE(OP_OCLASS, B); 452 | fprintf(fp, "OP_OCLASS\tR%"PRId32"\t\t", a); 453 | print_lv_a(mrb, irep, a); 454 | break; 455 | CASE(OP_CLASS, BB); 456 | fprintf(fp, "OP_CLASS\tR%"PRId32"\t:%s", a, pico_mrb_sym_dump(mrb, b)); 457 | print_lv_a(mrb, irep, a); 458 | break; 459 | CASE(OP_MODULE, BB); 460 | fprintf(fp, "OP_MODULE\tR%"PRId32"\t:%s", a, pico_mrb_sym_dump(mrb, b)); 461 | print_lv_a(mrb, irep, a); 462 | break; 463 | CASE(OP_EXEC, BB); 464 | fprintf(fp, "OP_EXEC\tR%"PRId32"\tI(%d:%p)", a, b, NULL); 465 | print_lv_a(mrb, irep, a); 466 | break; 467 | CASE(OP_SCLASS, B); 468 | fprintf(fp, "OP_SCLASS\tR%"PRId32"\t", a); 469 | print_lv_a(mrb, irep, a); 470 | break; 471 | CASE(OP_TCLASS, B); 472 | fprintf(fp, "OP_TCLASS\tR%"PRId32"\t\t", a); 473 | print_lv_a(mrb, irep, a); 474 | break; 475 | CASE(OP_ERR, B); 476 | // if ((irep->pool[a].tt & IREP_TT_NFLAG) == 0) { 477 | // fprintf(fp, "OP_ERR\t%s\n", irep->pool[a].u.str); 478 | // } 479 | // else { 480 | fprintf(fp, "OP_ERR\tL(%"PRId32")\n", a); 481 | // } 482 | break; 483 | CASE(OP_EXCEPT, B); 484 | fprintf(fp, "OP_EXCEPT\tR%"PRId32"\t\t", a); 485 | print_lv_a(mrb, irep, a); 486 | break; 487 | CASE(OP_RESCUE, BB); 488 | fprintf(fp, "OP_RESCUE\tR%"PRId32"\tR%d", a, b); 489 | print_lv_ab(mrb, irep, a, b); 490 | break; 491 | CASE(OP_RAISEIF, B); 492 | fprintf(fp, "OP_RAISEIF\tR%"PRId32"\t\t", a); 493 | print_lv_a(mrb, irep, a); 494 | break; 495 | 496 | CASE(OP_DEBUG, BBB); 497 | fprintf(fp, "OP_DEBUG\t%"PRId32"\t%d\t%d\n", a, b, c); 498 | break; 499 | 500 | CASE(OP_STOP, Z); 501 | fprintf(fp, "OP_STOP\n"); 502 | break; 503 | 504 | default: 505 | fprintf(fp, "OP_unknown (0x%x)\n", ins); 506 | // return; 507 | break; 508 | } 509 | fprintf(fp, "\n"); 510 | } 511 | } 512 | 513 | int 514 | Dump_mrbDump(FILE *fp, Scope *scope, const char *initname) 515 | { 516 | if (initname[0] == '\0') { 517 | fwrite(scope->vm_code, scope->vm_code_size, 1, fp); 518 | } else { 519 | int i; 520 | for (i=0; i < 5; i++) { 521 | fwrite(C_FORMAT_LINES[i], strlen(C_FORMAT_LINES[i]), 1, fp); 522 | if (i == 2) { 523 | fwrite(initname, strlen(initname), 1, fp); 524 | fwrite("[];", 3, 1, fp); 525 | } 526 | if (i < 4) fwrite("\n", 1, 1, fp); 527 | } 528 | fwrite(initname, strlen(initname), 1, fp); 529 | fwrite("[] = {", 6, 1, fp); 530 | char buf[6]; 531 | for (i = 0; i < scope->vm_code_size; i++) { 532 | if (i % 16 == 0) fwrite("\n", 1, 1, fp); 533 | snprintf(buf, 6, "0x%02x,", scope->vm_code[i]); 534 | fwrite(buf, 5, 1, fp); 535 | } 536 | fwrite("\n};", 3, 1, fp); 537 | } 538 | return 0; 539 | } 540 | 541 | #define MRB_FLOAT_FMT "%.17g" 542 | 543 | static int 544 | cdump_pool(const Literal *p, FILE *fp) 545 | { 546 | if (p->type & IREP_TT_NFLAG) { /* number */ 547 | switch (p->type) { 548 | #ifdef MRB_64BIT 549 | case INT64_LITERAL: 550 | if (p->value < INT32_MIN || INT32_MAX < p->value) { 551 | fprintf(fp, "{IREP_TT_INT64, {.i64=%" PRId64 "}},\n", p->value); 552 | } 553 | else { 554 | /* Deprecated? INT32 */ 555 | fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", (int32_t)p->value); 556 | } 557 | break; 558 | #endif 559 | case INT32_LITERAL: 560 | /* Deprecated? INT32 */ 561 | //fprintf(fp, "{IREP_TT_INT32, {.i32=%" PRId32 "}},\n", p->value); 562 | break; 563 | case FLOAT_LITERAL: 564 | #ifndef MRB_NO_FLOAT 565 | if (p->value == 0) { 566 | fprintf(fp, "{IREP_TT_FLOAT, {.f=%#.1f}},\n", atof(p->value)); 567 | } 568 | else { 569 | fprintf(fp, "{IREP_TT_FLOAT, {.f=" MRB_FLOAT_FMT "}},\n", atof(p->value)); 570 | } 571 | #endif 572 | break; 573 | case BIGINT_LITERAL: 574 | { 575 | const char *s = p->value; 576 | int len = strlen(s); 577 | fputs("{IREP_TT_BIGINT, {\"", fp); 578 | for (int i=0; itype>>2; 588 | const char *s = p->value; 589 | fprintf(fp, "{IREP_TT_STR|(%d<<2), {\"", len); 590 | for (i=0; i= 2 && name[len-1] == '=' && sym_name_word_p(name, len-1); 642 | } 643 | 644 | static bool 645 | sym_name_with_question_mark_p(const char *name, int len) 646 | { 647 | return len >= 2 && name[len-1] == '?' && sym_name_word_p(name, len-1); 648 | } 649 | 650 | static bool 651 | sym_name_with_bang_p(const char *name, int len) 652 | { 653 | return len >= 2 && name[len-1] == '!' && sym_name_word_p(name, len-1); 654 | } 655 | 656 | static bool 657 | sym_name_ivar_p(const char *name, int len) 658 | { 659 | return len >= 2 && name[0] == '@' && sym_name_word_p(name+1, len-1); 660 | } 661 | 662 | static bool 663 | sym_name_cvar_p(const char *name, int len) 664 | { 665 | return len >= 3 && name[0] == '@' && sym_name_ivar_p(name+1, len-1); 666 | } 667 | 668 | #define OPERATOR_SYMBOL(sym_name, name) {name, sym_name, sizeof(sym_name)-1} 669 | struct operator_symbol { 670 | const char *name; 671 | const char *sym_name; 672 | uint16_t sym_name_len; 673 | }; 674 | static const struct operator_symbol operator_table[] = { 675 | OPERATOR_SYMBOL("!", "not"), 676 | OPERATOR_SYMBOL("%", "mod"), 677 | OPERATOR_SYMBOL("&", "and"), 678 | OPERATOR_SYMBOL("*", "mul"), 679 | OPERATOR_SYMBOL("+", "add"), 680 | OPERATOR_SYMBOL("-", "sub"), 681 | OPERATOR_SYMBOL("/", "div"), 682 | OPERATOR_SYMBOL("<", "lt"), 683 | OPERATOR_SYMBOL(">", "gt"), 684 | OPERATOR_SYMBOL("^", "xor"), 685 | OPERATOR_SYMBOL("`", "tick"), 686 | OPERATOR_SYMBOL("|", "or"), 687 | OPERATOR_SYMBOL("~", "neg"), 688 | OPERATOR_SYMBOL("!=", "neq"), 689 | OPERATOR_SYMBOL("!~", "nmatch"), 690 | OPERATOR_SYMBOL("&&", "andand"), 691 | OPERATOR_SYMBOL("**", "pow"), 692 | OPERATOR_SYMBOL("+@", "plus"), 693 | OPERATOR_SYMBOL("-@", "minus"), 694 | OPERATOR_SYMBOL("<<", "lshift"), 695 | OPERATOR_SYMBOL("<=", "le"), 696 | OPERATOR_SYMBOL("==", "eq"), 697 | OPERATOR_SYMBOL("=~", "match"), 698 | OPERATOR_SYMBOL(">=", "ge"), 699 | OPERATOR_SYMBOL(">>", "rshift"), 700 | OPERATOR_SYMBOL("[]", "aref"), 701 | OPERATOR_SYMBOL("||", "oror"), 702 | OPERATOR_SYMBOL("<=>", "cmp"), 703 | OPERATOR_SYMBOL("===", "eqq"), 704 | OPERATOR_SYMBOL("[]=", "aset"), 705 | }; 706 | 707 | static const char* 708 | sym_operator_name(const char *sym_name, int len) 709 | { 710 | uint32_t table_size = sizeof(operator_table)/sizeof(struct operator_symbol); 711 | if (operator_table[table_size-1].sym_name_len < len) return NULL; 712 | 713 | uint32_t start, idx; 714 | int cmp; 715 | const struct operator_symbol *op_sym; 716 | for (start = 0; table_size != 0; table_size/=2) { 717 | idx = start+table_size/2; 718 | op_sym = &operator_table[idx]; 719 | cmp = (int)len-(int)op_sym->sym_name_len; 720 | if (cmp == 0) { 721 | cmp = memcmp(sym_name, op_sym->sym_name, len); 722 | if (cmp == 0) return op_sym->name; 723 | } 724 | if (0 < cmp) { 725 | start = ++idx; 726 | --table_size; 727 | } 728 | } 729 | return NULL; 730 | } 731 | 732 | #define IS_EVSTR(p,e) ((p) < (e) && (*(p) == '$' || *(p) == '@' || *(p) == '{')) 733 | const char picorb_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 734 | 735 | static void 736 | picorb_str_dump(char *result, const char *str, const uint32_t len) 737 | { 738 | result[0] = '"'; 739 | result[1] = '\0'; 740 | const char *p, *pend; 741 | char buf[5]; /* `\x??` or UTF-8 character */ 742 | 743 | p = str; pend = str + len; 744 | for (;p < pend; p++) { 745 | memset(buf, 0, 5); 746 | unsigned char c, cc; 747 | c = *p; 748 | if (c == '"'|| c == '\\' || (c == '#' && IS_EVSTR(p+1, pend))) { 749 | buf[0] = '\\'; buf[1] = c; 750 | strsafecat(result, buf, 1024); 751 | continue; 752 | } 753 | if (ISPRINT(c)) { 754 | buf[0] = c; 755 | strsafecat(result, buf, 1024); 756 | continue; 757 | } 758 | switch (c) { 759 | case '\n': cc = 'n'; break; 760 | case '\r': cc = 'r'; break; 761 | case '\t': cc = 't'; break; 762 | case '\f': cc = 'f'; break; 763 | case '\013': cc = 'v'; break; 764 | case '\010': cc = 'b'; break; 765 | case '\007': cc = 'a'; break; 766 | case 033: cc = 'e'; break; 767 | default: cc = 0; break; 768 | } 769 | if (cc) { 770 | buf[0] = '\\'; 771 | buf[1] = (char)cc; 772 | strsafecat(result, buf, 1024); 773 | continue; 774 | } 775 | else { 776 | buf[0] = '\\'; 777 | buf[1] = 'x'; 778 | buf[3] = picorb_digitmap[c % 16]; c /= 16; 779 | buf[2] = picorb_digitmap[c % 16]; 780 | strsafecat(result, buf, 1024); 781 | continue; 782 | } 783 | } 784 | strsafecat(result, "\"", 1024); 785 | } 786 | static int 787 | cdump_sym(Symbol *sym, const char *var_name, int idx, char *init_syms_code, FILE *fp) 788 | { 789 | if (sym == 0) return PICORB_DUMP_INVALID_ARGUMENT; 790 | 791 | int len = sym->len; 792 | const char *name = sym->value, *op_name; 793 | if (!name) return PICORB_DUMP_INVALID_ARGUMENT; 794 | if (sym_name_word_p(name, len)) { 795 | fprintf(fp, "MRB_SYM(%s)", name); 796 | } 797 | else if (sym_name_with_equal_p(name, len)) { 798 | fprintf(fp, "MRB_SYM_E(%.*s)", (int)(len-1), name); 799 | } 800 | else if (sym_name_with_question_mark_p(name, len)) { 801 | fprintf(fp, "MRB_SYM_Q(%.*s)", (int)(len-1), name); 802 | } 803 | else if (sym_name_with_bang_p(name, len)) { 804 | fprintf(fp, "MRB_SYM_B(%.*s)", (int)(len-1), name); 805 | } 806 | else if (sym_name_ivar_p(name, len)) { 807 | fprintf(fp, "MRB_IVSYM(%s)", name+1); 808 | } 809 | else if (sym_name_cvar_p(name, len)) { 810 | fprintf(fp, "MRB_CVSYM(%s)", name+2); 811 | } 812 | else if ((op_name = sym_operator_name(name, len))) { 813 | fprintf(fp, "MRB_OPSYM(%s)", op_name); 814 | } 815 | else { 816 | char buf[32]; 817 | strsafecat(init_syms_code, " ", 1024); 818 | strsafecat(init_syms_code, var_name, 1024); 819 | snprintf(buf, sizeof(buf), "[%d] = ", idx); 820 | strsafecat(init_syms_code, buf, 1024); 821 | strsafecat(init_syms_code, "mrb_intern_lit(mrb, ", 1024); 822 | char *result = picorbc_alloc(1024); 823 | picorb_str_dump(result, sym->value, sym->len); 824 | strsafecat(init_syms_code, result, 1024); 825 | picorbc_free(result); 826 | strsafecat(init_syms_code, ");\n", 1024); 827 | fputs("0", fp); 828 | } 829 | fputs(", ", fp); 830 | return PICORB_DUMP_OK; 831 | } 832 | 833 | static int 834 | cdump_syms(const char *name, const char *key, int n, int syms_len, Symbol *syms, char *init_syms_code, FILE *fp) 835 | { 836 | // int ai = mrb_gc_arena_save(mrb); 837 | int code_len = strlen(init_syms_code); 838 | const char *var_name = sym_var_name(name, key, n); 839 | fprintf(fp, "mrb_DEFINE_SYMS_VAR(%s, %d, (", var_name, syms_len); 840 | Symbol *sym = syms; 841 | for (int i=0; inext; 844 | } 845 | picorbc_free((void *)var_name); 846 | fputs("), ", fp); 847 | if (code_len == strlen(init_syms_code)) fputs("const", fp); 848 | fputs(");\n", fp); 849 | // mrb_gc_arena_restore(mrb, ai); 850 | return PICORB_DUMP_OK; 851 | } 852 | 853 | int 854 | cdump_irep_struct(Scope *scope, uint8_t flags, FILE *fp, const char *name, int n, char *init_syms_code, int *mp) 855 | { 856 | int i, len; 857 | int max = *mp; 858 | int debug_available = 0; 859 | /* dump reps */ 860 | if (scope->first_lower) { 861 | Scope *next = scope->first_lower; 862 | for (i=0,len=scope->nlowers; inext; 868 | } 869 | fprintf(fp, "static const mrb_irep *%s_reps_%d[%d] = {\n", name, n, len); 870 | for (i=0,len=scope->nlowers; iliteral) { 877 | len=scope->plen; 878 | fprintf(fp, "static const mrb_pool_value %s_pool_%d[%d] = {\n", name, n, len); 879 | Literal *lit = scope->literal; 880 | for (i=0; inext; 884 | } 885 | fputs("};\n", fp); 886 | } 887 | /* dump syms */ 888 | if (scope->symbol) { 889 | cdump_syms(name, "syms", n, scope->slen, scope->symbol, init_syms_code, fp); 890 | } 891 | /* dump iseq */ 892 | len = scope->ilen + sizeof(ExcHandler) * scope->clen; 893 | fprintf(fp, "static const mrb_code %s_iseq_%d[%d] = {", name, n, len); 894 | for (i=0; iupper == NULL) { 897 | fprintf(fp, "0x%02x,", scope->vm_code[i + MRB_HEADER_SIZE + IREP_HEADER_SIZE]); 898 | } else { 899 | if (scope->vm_code) { 900 | fprintf(fp, "0x%02x,", scope->vm_code[i + IREP_HEADER_SIZE]); 901 | } 902 | } 903 | } 904 | fputs("};\n", fp); 905 | /* dump lv */ 906 | if (scope->lvar) { 907 | cdump_syms(name, "lv", n, scope->nlocals-1, (Symbol*)(scope->lvar), init_syms_code, fp); 908 | } 909 | /* dump debug */ 910 | // TODO 911 | /* dump irep */ 912 | fprintf(fp, "static const mrb_irep %s_irep_%d = {\n", name, n); 913 | fprintf(fp, " %d,%d,%d,\n", scope->nlocals, scope->sp, scope->clen); 914 | fprintf(fp, " MRB_IREP_STATIC,%s_iseq_%d,\n", name, n); 915 | if (scope->literal) { 916 | fprintf(fp, " %s_pool_%d,", name, n); 917 | } 918 | else { 919 | fputs( " NULL,", fp); 920 | } 921 | if (scope->symbol) { 922 | fprintf(fp, "%s_syms_%d,", name, n); 923 | } 924 | else { 925 | fputs( "NULL,", fp); 926 | } 927 | if (scope->first_lower) { 928 | fprintf(fp, "%s_reps_%d,\n", name, n); 929 | } 930 | else { 931 | fputs( "NULL,\n", fp); 932 | } 933 | if (scope->lvar) { 934 | fprintf(fp, " %s_lv_%d,\n", name, n); 935 | } 936 | else { 937 | fputs( " NULL,\t\t\t\t\t/* lv */\n", fp); 938 | } 939 | if(debug_available) { 940 | fprintf(fp, " &%s_debug_%d,\n", name, n); 941 | } 942 | else { 943 | fputs(" NULL,\t\t\t\t\t/* debug_info */\n", fp); 944 | } 945 | fprintf(fp, " %"PRId32",%d,%d,%d,0\n};\n", scope->ilen, scope->plen, scope->slen, scope->nlowers); 946 | 947 | return PICORB_DUMP_OK; 948 | } 949 | 950 | int 951 | Dump_cstructDump(FILE *fp, Scope *scope, uint8_t flags, const char *initname) 952 | { 953 | if (fp == NULL || initname == NULL || initname[0] == '\0') { 954 | return PICORB_DUMP_INVALID_ARGUMENT; 955 | } 956 | if (fprintf(fp, "#include \n" 957 | "#include \n" 958 | "#include \n" 959 | "\n") < 0) { 960 | return PICORB_DUMP_WRITE_FAULT; 961 | } 962 | fputs("#define mrb_BRACED(...) {__VA_ARGS__}\n", fp); 963 | fputs("#define mrb_DEFINE_SYMS_VAR(name, len, syms, qualifier) \\\n", fp); 964 | fputs(" static qualifier mrb_sym name[len] = mrb_BRACED syms\n", fp); 965 | fputs("\n", fp); 966 | char *init_syms_code = (char *)picorbc_alloc(1024); // FIXME 967 | init_syms_code[0] = '\0'; 968 | int max = 1; 969 | int n = cdump_irep_struct(scope, flags, fp, initname, 0, init_syms_code, &max); 970 | if (n != PICORB_DUMP_OK) return n; 971 | fprintf(fp, 972 | "%s\n" 973 | "const struct RProc %s[] = {{\n", 974 | (flags & PICORB_DUMP_STATIC) ? "static" 975 | : "#ifdef __cplusplus\n" 976 | "extern const struct RProc test[];\n" 977 | "#endif", 978 | initname); 979 | fprintf(fp, "NULL,NULL,MRB_TT_PROC,7,0,{&%s_irep_0},NULL,{NULL},\n}};\n", initname); 980 | fputs("static void\n", fp); 981 | fprintf(fp, "%s_init_syms(mrb_state *mrb)\n", initname); 982 | fputs("{\n", fp); 983 | fputs(init_syms_code, fp); 984 | picorbc_free(init_syms_code); 985 | fputs("}\n", fp); 986 | return PICORB_DUMP_OK; 987 | } 988 | --------------------------------------------------------------------------------