4 prism_languages: [ruby]
9 Goby's language design is based on Ruby language's, slim and shaped up. Differences in syntax between them is very small.
23 attr_accessor :audience, :head, :tail
35 puts head + name + tail
43 @name = self.class.to_s
52 greet.audience = World.new
69 * `reset`: reset the VM
72 * ctrl-c: cancel the block entered, or exit (on top level)
74 See [igb manual & test script](https://github.com/goby-lang/goby/blob/master/igb/manual_test.md). You can use `readline` features such as command history by arrow keys.
86 Should be "`[a-z][a-z0-9_]+`"(snake_case).
93 @state = state # declaring an instance variable by assignment
96 @state # accessible from other instance methods
100 state = State.new "success"
105 Should be "`@[a-z][a-z0-9_]+`"(snake_case).
107 ### Multiple assignment
117 # array literal with '*'
120 # bare assignment: unsupported
121 a, b, c = 1, 2, 3 #=> unexpected 3 Line: 0
124 ### Black hole variable
141 ### Method definition and calling
145 if baz == "Hi, Goby!"
152 foo_bar? "Hi, Goby!" #=> true
155 Method name should be "`[a-z][a-z0-9_]+\??`" (snake_case). You can omit the trailing "`()`" only if no parameters are taken. Trailing using "`!`" is **unsupported**.
157 ### Order of method parameter
160 def foo(normal, default="value", hash={}, ary=[], keyword:, keyword_default:"key", *sprat)
164 If a default value is provided to a parameter, the parameter can be omitted when calling. `()` can be omitted. The order of parameters in method definition is restricted as follows:
166 1. **normal parameters** (like `a`)
167 2. **normal parameters with default value** (like `a=1`)
168 3. **optional parameters** (array or hash, like `ary=[]` or `hs={}`)
169 4. **keyword parameters** (like `kwd:`)
170 5. **keyword parameters with default value** (like `kwd: 1` or `ary: [1,2,3]` or `hsh: {key: "value"}`)
171 6. **splat parameters** (like `*sp`)
173 Or you will receive an error.
175 ### Keyword parameter (WIP)
178 def foo(process:, verb: :GET, opt:{ csp: :enabled }, ary: [1, 2, 3])
187 radius * PI # returns the result of evaluation
193 ### Returning multiple value
200 my_array #=> [1, 2, 3]
207 def bar # defining instance method
211 def baz(count, email: "goby@example.com")
226 ### Singleton method #1
230 def str.foo #1 singleton method on the object
238 ### Singleton method #2
242 def self.bar #2 singleton method with `self.`
248 ### Singleton method #3
252 def Foo.bar #3 singleton method with a class name (unrecommended)
258 ### Singleton method #4
263 def Foo.bar #4 singleton methods outside the Foo
270 ### Attribute accessor method
274 attr_accessor :bar, :baz
288 You can use the following shorthands to declare attribute accessor methods in classes/modules:
294 ### Private method (to be implemented)
302 def _baz # leading '_' means private method
308 ## Module/Class definition
311 ### Module definition and `include`
316 "Foo's instance method"
321 include Foo # to include Foo
324 Bar.new.foo #=> Foo's instance method
327 Module names should be "`[A-Z][A-Za-z0-9_]+`" (UpperCamelCase). Modules cannot be inherited.
329 ### Module definition and `extend`
334 "Foo's instance method will be a singleton method"
339 extend Foo # to extend Foo
342 Bar.foo #=> Foo's instance method will be a singleton method
345 `extend` is to use the instance methods in the specified modules as **singleton methods** in your class or module.
347 ### Module instantiation
350 module Foo #module definition
359 Actually, Goby's module can be even **instantiated** via "`new`" like "`Foo.new`".
361 ### Class definition and inheritance
364 class Foo # class definition
370 class Baz < Foo # inheritance
376 Class names should be "`[A-Z][A-Za-z0-9]+`" (UpperCamelCase). Inheritance with "`<`" is supported.
382 HTTP_ERROR_404 = 500 # error
385 Constants should be "`[A-Z][A-Za-z0-9_]+`" (UPPER_SNAKECASE). Constants are **not reentrant** and the scope is **global**.
387 ### Redefining class/modules
397 def bar # redefining is possible
415 Foo::Bar.new.baz # Use '::' for namespacing
416 Foo::Bar::MAGIC # Use '::' for namespacing
424 require("uri") # to activate URL class
426 u = URI.parse("http://example.com")
430 ### `require_relative`
433 require_relative("bar") # loading the local bar.gb
453 `def`, `true`, `false`, `nil`, `if`, `elsif`, `else`, `case`, `when`, `return`, `self`, `end`, `while`, `do`, `yield`, `get_block`, `next`, `class`, `module`, `break`
462 Double and single quotation can be used.
467 :symbol # equivalent to "symbol"
471 Goby's symbol (using `:`) is always `String` class.
476 year = 2018 # Integer
477 offset = -42 # Integer
485 [1, 2, 3, "hello", :goby, { key: "value"}]
492 h = { key: "value", key2: "value2" }
496 Hash literal's keys should always be **symbol literals**.
501 (1..10).each do |x| # '..' represents a range
506 ### Boolean and `nil`
510 false # Boolean class
516 Any objects except `nil` and `false` will be treated as `true` on conditionals.
520 ### Arithmetic/logical/assignment operators
526 * / % # multiplication, division, modulus
527 + - # addition, subtraction
528 ! # logical inversion
529 > >= < <= # inequality comparison
530 == != # equality comparison, negative comparison
533 += -= # shorthand of addition/subtraction
537 *Priority of operators are TBD
542 () # chaning priority of interpretation
544 * # multiple assignment
548 *Priority of operators are TBD
553 class Foo; end # ';' to delimit
555 class Bar end # recommended
558 ### String interpolation (to be implemented)
561 puts "Error: #{error_message}" # double quotation is required
567 puts "Goby" # comments
570 Use the annotations to keep the comments concise.
582 * special constants: `ARGV`, `STDIN`, `STDOUT`, `STDERR`, `ENV`
587 ### `if`, `else`, `elsif`
601 `then` is **not** supported.
607 (5..tail).each do |t|
608 if t % 2 == 0 && t % 5 == 0
610 break # finish the block
615 puts "out of the block"
630 puts "You might be Aragorn II!"
632 puts "Long time no see, Aragorn!"
633 when "Frodo", "Sam", "Gandalf"
636 puts "You're not yourself"
654 `while`, conditional and a `do`/`end` block can be used for a loop.
658 Under construction. Join [#605](https://github.com/goby-lang/goby/issues/605).
666 def foo(ary: [1, 2, 3])
667 ary.each do |s| # start of the block with |block variable|
669 end # end of the block
673 `{ }` cannot be used for forming a block.
679 yield(10) # executes the block given
687 ### Block object and `call`
697 `Block.new` can take a block and then `call`.
707 def exec_block(block)
724 ### Passing a block with block arguments
727 b = Block.new do |arg, offset|
731 b.call(49, 500) #=> 549
734 ### Special `get_block` keyword
738 # runs the block object and the block arg simultaneously
739 block.call + get_block.call
743 bar(get_block) do # passes two blocks to `bar`
753 `get_block` is not a method but a **keyword** to retrive a given block argument as a block object. By this, you can pass around or `call` the given block arguments as block objects.
758 count = 0 # the declaration is used
760 count += 1 # the block looks preserving the `count`
765 count = 9 # (does not affect)
766 puts blk.call # local variable is resolved to the one above
775 ## Native class (Primary)
778 Goby's most "native" classes cannot instantiate with `new` in principle.
784 #» [Bar, Foo, Object]
785 Bar.singleton_class.ancestors
786 #» [#<Class:Bar>, #<Class:Object>, Class, Object]
789 `Object` is actually just for creating singleton classes. See `Class`.
791 * **`Object.methods`**: `!`, `!=`, `==`, `block_given?`, `class`, `exit`, `instance_eval`, `instance_variable_get`, `instance_variable_set`, `is_a?`, `methods`, `nil?`, `object_id`, `puts`, `raise`, `require`, `require_relative`, `send`, `singleton_class`, `sleep`, `thread`, `to_s`, `<`, `<=`, `>`, `>=`, `ancestors`, `attr_accessor`, `attr_reader`, `attr_writer`, `extend`, `include`, `name`, `new`, `superclass`
793 * **`Object.new.methods`**: `!`, `!=`, `==`, `block_given?`, `class`, `exit`, `instance_eval`, `instance_variable_get`, `instance_variable_set`, `is_a?`, `methods`, `nil?`, `object_id`, `puts`, `raise`, `require`, `require_relative`, `send`, `singleton_class`, `sleep`, `thread`, `to_s`
798 String.ancestors #=> [String, Object]
801 `Class` and `Object`can actually be regarded as the same and you don't need to distinguish them in almost all the cases.
803 * **`Class.methods`**: `<`, `<=`, `>`, `>=`, `ancestors`, `attr_accessor`, `attr_reader`, `attr_writer`, `extend`, `include`, `name`, `new`, `superclass`, `!`, `!=`, `==`, `block_given?`, `class`, `exit`, `instance_eval`, `instance_variable_get`, `instance_variable_set`, `is_a?`, `methods`, `nil?`, `object_id`, `puts`, `raise`, `require`, `require_relative`, `send`, `singleton_class`, `sleep`, `thread`, `to_s`
808 puts "Hello" + ' ' + 'world' #=> Hello world
811 Fixed to **UTF-8** with mb4 support.
813 * **`String.methods`**: `fmt`,
814 * the rest: `Class.methods`
815 * **`"a".methods`**: `!=`, `*`, `+`, `<`, `<=>`, `==`, `=~`, `>`, `[]`, `[]=`, `capitalize`, `chop`, `concat`, `count`, `delete`, `downcase`, `each_byte`, `each_char`, `each_line`, `empty?`, `end_with?`, `eql?`, `fmt`, `include?`, `insert`, `length`, `ljust`, `match`, `new`, `replace`, `replace_once`, `reverse`, `rjust`, `size`, `slice`, `split`, `start_with`, `strip`, `to_a`, `to_bytes`, `to_d`, `to_f`, `to_i`, `to_s`, `upcase`,
816 * the rest: `Object.new.methods`
821 37037 * 27 #=> 999999
824 * **`Integer.methods`**: the same as `Class.methods`
825 * **`1.methods`**: `!=`, `%`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `even?`, `new`, `next`, `odd?`, `pred`, `ptr`, `times`, `to_f`, `to_float32`, `to_float64`, `to_i`, `to_int`, `to_int16`, `to_int32`, `to_int64`, `to_int8`, `to_s`, `to_uint`, `to_uint16`, `to_uint32`, `to_uint64`, `to_uint8`
826 * the rest: `Object.new.methods`
831 [1, "2", :card, [4, 5], { john: "doe" }]
834 * **`Array.methods`**: the same as `Class.methods`
835 * **`[1].methods`**: `*`, `+`, `[]`, `[]=`, `any?`, `at`, `clear`, `concat`, `count`, `delete_at`, `dig`, `each`, `each_index`, `empty?`, `first`, `flatten`, `include?`, `join`, `last`, `lazy`, `length`, `map`, `new`, `pop`, `push`, `reduce`, `reverse`, `reverse_each`, `rotate`, `select`, `shift`, `to_enum`, `unshift`, `values_at`
836 * the rest: `Object.new.methods`
842 h = { "key": "value" } #=> error
848 Keys in hash literals should be **symbol literals**, while Hash index can be either string or symbol literals.
850 * **`Hash.methods`**: the same as `Class.methods`
851 * **`{ key: "value" }.methods`**: `[]`, `[]=`, `any?`, `clear`, `default`, `default=`, `delete`, `delete_if`, `dig`, `each`, `each_key`, `each_value`, `empty?`, `eql?`, `fetch`, `fetch_values`, `has_key?`, `has_value?`, `keys`, `length`, `map_values`, `merge`, `new`, `select`, `sorted_keys`, `to_a`, `to_json`, `to_s`, `transform_values`, `values`, `values_at`
852 * the rest: `Object.new.methods`
862 * **`Range.methods`**: the same as `Class.methods`
863 * **`(1..10).methods`**: `!=`, `==`, `bsearch`, `each`, `first`, `include?`, `last`, `lazy`, `map`, `new`, `size`, `step`, `to_a`, `to_enum`
864 * the rest: `Object.new.methods`
876 * **`Block.methods`**: the same as `Class.methods`
877 * **`(Block.new do end).methods`**: `call`
878 * the rest: `Object.new.methods`
880 ## Native class (secondary)
887 2.1 * -2.1 # => -4.41
890 Float literals like `3.14` or `-273.15`. `Float` class is based on Golang's `float64` type.
892 * **`Float.methods`**: the same as `Class.methods`
893 * **`3.14.methods`**: `!=`, `%`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `new`, `ptr`, `to_d`, `to_i`
894 * the rest: `Object.new.methods`
899 "3.14".to_d # => 3.14
900 "-0.7238943".to_d # => -0.7238943
902 # => 3.1415929203539823008849557522123893805309734513274336283185840
910 ('16.1'.to_d + "1.1".to_d).to_s #=> 17.2
911 ('16.1'.to_f + "1.1".to_f).to_s #=> 17.200000000000003
914 Experimental: the size is arbitrary and internally a fraction from Golang's `big.Rat` type. Decimal literal is TBD for now and you can get `Decimal` number via `to_d` method from `Integer`/`Float`/`String`.
916 * **`Decimal.methods`**: the same as `Class.methods`
917 * **`(1.1).to_d.methods`**: `!=`, `*`, `**`, `+`, `-`, `/`, `<`, `<=`, `<=>`, `==`, `>`, `>=`, `denominator`, `fraction`, `inverse`
918 * the rest: `Object.new.methods`
923 a = Regexp.new("orl")
924 a.match?("Hello World") #=> true
925 a.match?("Hello Regexp") #=> false
928 b.match?("🤡 😏 😐") #=> true
929 b.match?("😝 😍 😊") #=> false
931 c = Regexp.new("居(ら(?=れ)|さ(?=せ)|る|ろ|れ(?=[ばる])|よ|(?=な[いかくけそ]|ま[しすせ]|そう|た|て))")
932 c.match?("居られればいいのに") #=> true
933 c.match?("居ずまいを正す") #=> false
936 Using `/ /` is to be implemented.
938 * **`Regexp.methods`**: the same as `Class.methods`
939 * **`Regexp.new("^aa$").methods`**: `==`, `match?`
940 * the rest: `Object.new.methods`
946 'abcd'.match(Regexp.new('(b.)'))
947 #=> #<MatchData 0:"bc" 1:"bc">
950 'abcd'.match(Regexp.new('a(?<first>b)(?<second>c)'))
951 #=> #<MatchData 0:"abc" first:"b" second:"c">
954 » 'abcd'.match(Regexp.new('a(?<first>b)(?<second>c)')).to_h
955 #» { 0: "abc", first: "b", second: "c" }
958 The number keys in the captures are actually `String` class.The key `0` is the mached string.
960 * **`MatchData.methods`**: the same as `Class.methods`
961 * **`'abcd'.match(Regexp.new('(b.)')).methods`**: `captures`, `length`, `new`, `to_a`, `to_h`
962 * the rest: `Object.new.methods`
967 f = File.new("../test_fixtures/file_test/size.gb")
968 f.name #=> "../test_fixtures/file_test/size.gb"
971 * **`File.methods`**: `basename`, `chmod`, `delete`, `exist?`, `extname`, `join`
972 * the rest: `Class.methods`
973 * **`File.new.methods`**: `basename`, `chmod`, `close`, `delete`, `exist?`, `extname`, `join`, `name`
974 * the rest: `Object.new.methods`
976 ## Native class (Golang-oriented)
983 m = GoMap.new(h) # to pass values to Golang's code
988 * **`GoMap.methods`**: the same as `Class.methods`
989 * **`GoMap.new.methods`**: `get`, `set`, `to_hash`
990 * the rest: `Object.new.methods`
997 1001.times do |i| # i start from 0 to 1000
1011 `Channel` class is to hold channels to work with `#thread`. See `thread`.
1013 * **`Channel.methods`**: the same as `Class.methods`
1014 * **`Channel.new.methods`**: `close`, `deliver`, `new`, `receive`
1015 * the rest: `Object.new.methods`
1017 ## Enumerator & lazy
1019 Pretty new experimental library.
1021 ### `LazyEnumerator`
1024 # creating a lazy enumerator
1025 enumerator = LazyEnumerator.new(ArrayEnumerator.new([1, 2, 3])) do |value|
1030 enumerator.each do |value|
1034 result #=> [2, 4, 6]
1037 A shorthand `#lazy` method is also provided in `Array` and `Range` by now. See "Tips & tricks" below.
1039 * **`LazyEnumerator.methods`**: the same as `Class.methods`
1040 * **`[1, 2].lazy`**: `each`, `first`, `has_next?`, `initialize`, `map`, `next`
1041 * the rest: `Object.new.methods`
1043 ### `ArrayEnumerator`
1046 iterated_values = []
1048 enumerator = ArrayEnumerator.new([1, 2, 4])
1050 while enumerator.has_next? do
1051 iterated_values.push(enumerator.next)
1054 iterated_values #=> [1, 2, 4]
1057 * **`ArrayEnumerator.methods`**: the same as `Class.methods`
1058 * **`ArrayEnumerator.new([1, 2, 3]).methods`**: `has_next?`, `initialize`, `next`
1059 * the rest: `Object.new.methods`
1061 ### `RangeEnumerator`
1064 iterated_values = []
1066 enumerator = RangeEnumerator.new((1..4))
1068 while enumerator.has_next? do
1069 iterated_values.push(enumerator.next)
1072 iterated_values #=> [1, 2, 3, 4]
1075 * **`RangeEnumerator.methods`**: the same as `Class.methods`
1076 * **`RangeEnumerator.new(1..2).methods`**: `has_next?`, `initialize`, `next`
1077 * the rest: `Object.new.methods`
1084 true.class #=> Boolean
1085 false.class #=> Boolean
1088 A special class that just to hold `true` and `false`. Cannot be instantiate.
1096 A special class that just to hold `nil`. Cannot be instantiate.
1100 (A special dummy class that just holds methods defined by Goby code.)
1104 Provides `#dig` method. Currently. `Array` and `Hash` classes' instance can be `Diggable`.
1107 [1, 2].dig(0, 1) #=> TypeError: Expect target to be Diggable, got Integer
1110 ## Testing framework
1117 Spec.describe Spec do
1118 it "fails and exit with code 1" do
1126 * **`Spec.methods`**: `describe`, `describes`, `instance`, `run`
1127 * the rest: `Object.methods`
1128 * **`Spec.new.methods`**: `describes`, `initialize`, `run`, `session_successful`, `session_successful=`
1129 * the rest: `Hash.new.methods`
1137 #» ["!=", "*", "+", "<", "<=>", "==", "=~", ">", "[]", "[]=", "capitalize", "chop", "concat", "count", "delete", "downcase", "each_byte", "each_char", "each_line", "empty?", "end_with?", "eql?", "fmt", "include?", "insert", "length", "ljust", "match", "new", "replace", "replace_once", "reverse", "rjust", "size", "slice", "split", "start_with", "strip", "to_a", "to_bytes", "to_d", "to_f", "to_i", "to_s", "upcase", "!", "block_given?", "class", "exit", "instance_eval", "instance_variable_get", "instance_variable_set", "is_a?", "methods", "nil?", "object_id", "puts", "raise", "require", "require_relative", "send", "singleton_class", "sleep", "thread"]
1147 ### Showing singleton class
1150 » "moji".singleton_class
1151 #» #<Class:#<String:842352325152>>
1153 » "moji".class.singleton_class
1157 ### Showing ancestors
1161 #» [Integer, Object]
1163 » "moji".class.ancestors
1167 ### Showing singleton classes' ancestors
1170 » "moji".class.singleton_class.ancestors
1171 #» [#<Class:String>, #<Class:Object>, Class, Object]
1174 ### Showing object's id
1184 h = { a: 1, b: [1, "2", [4, 5, nil]]}
1185 h.to_json # converts hash to JSON
1186 #=> {"a":1, "b":[1, "2", [4, 5, null]]}
1189 ### Customize `#to_json`
1191 Overwrite the `#to_json` in your class:
1195 def initialize(name)
1200 { title: @name }.to_json
1205 def initialize(name, age)
1208 @job = JobTitle.new("software engineer")
1212 { name: @name, age: @age, job: @job }.to_json
1216 stan = Person.new("Stan", 23)
1217 h = { person: stan }
1218 h.to_json #=> {"person":{"name":"Stan","job":{"title":"software engineer"},"age":23}}
1221 ### Lazy enumeration
1223 To avoid N + 1 query.
1226 enumerator = [1, 2, 3].lazy.map do |value|
1231 enumerator.each do |value|
1235 result #=> [2, 4, 6]
1238 You can call `#lazy.map` on `Array`, `Range`, or `JSON` objects.
1242 ### Quick style guide
1244 * UTF-8 should be used.
1245 * Only two spaces ` ` should be used for one indentation.
1246 * Tab cannot be used for indentation.
1247 * For more, follow [RuboCop's style guide](https://github.com/bbatsov/ruby-style-guide) in principle.
1249 ### Document notation
1251 * `Class#instance_method` -- use `#` to represent instance methods in documents
1252 * `Class.class_method`
1253 * `Module.module_method`
1255 ### Syntax highlighting
1257 Ready for Vim and Sublime text. You can also use Ruby's syntax highlighting so far.
1263 * Official site: [https://goby-lang.org/](https://goby-lang.org/)
1264 * Repository: [https://github.com/goby-lang/goby/](https://github.com/goby-lang/goby/)
1265 * DevHints: [https://devhints.io/goby](https://devhints.io/goby) (this page)
1267 ### Readings for Goby developers
1269 * [Write an Interpreter in Go](https://interpreterbook.com/)
1270 * [Nand2Tetris II](https://www.coursera.org/learn/nand2tetris2/home/welcome)
1271 * [Ruby under a microscope](http://patshaughnessy.net/ruby-under-a-microscope)
1272 * [YARV's instruction table](http://www.atdot.net/yarv/insnstbl.html)
1276 * [Goby: Rubyライクな言語(1)Gobyを動かしてみる](https://techracho.bpsinc.jp/hachi8833/2017_11_10/47787)
1277 * [Gobyの組み込みクラスにメソッドを追加する方法](https://qiita.com/hanachin_/items/efc1c976a4f5749514ef)