mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Merge branch 'master' into stable
This commit is contained in:
@@ -22,7 +22,8 @@ jobs:
|
||||
- {os: ubuntu-22.04, cc: clang, cxx: clang++}
|
||||
- {os: macos-15, cc: clang, cxx: clang++}
|
||||
- {os: macos-14, cc: clang, cxx: clang++}
|
||||
- {os: windows-latest, cc: gcc, cxx: g++, altname: "mingw-gcc"}
|
||||
- {os: windows-2025, cc: gcc, cxx: g++, altname: "mingw-gcc"}
|
||||
- {os: windows-2022, cc: gcc, cxx: g++, altname: "mingw-gcc"}
|
||||
env:
|
||||
MRUBY_CONFIG: ci/gcc-clang
|
||||
CC: ${{ matrix.cc }}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Coverage
|
||||
|
||||
on: [push, pull_request]
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
|
||||
+22
-13
@@ -4,15 +4,19 @@ default_stages: [pre-commit, pre-push]
|
||||
default_language_version:
|
||||
# force all unspecified Python hooks to run python3
|
||||
python: python3
|
||||
node: 22.2.0
|
||||
# force all unspecified Node hooks to run Node.js v22.14.0 LTS
|
||||
node: 22.14.0
|
||||
minimum_pre_commit_version: "3.2.0"
|
||||
exclude: "^tools/lrama/"
|
||||
repos:
|
||||
- repo: meta
|
||||
hooks:
|
||||
- id: identity
|
||||
name: run identity check
|
||||
name: run identity
|
||||
description: check your identity
|
||||
- id: check-hooks-apply
|
||||
name: check hooks apply to the repository
|
||||
name: run check-hooks-apply
|
||||
description: check hooks apply to the repository
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: prettier
|
||||
@@ -21,17 +25,19 @@ repos:
|
||||
entry: prettier --write .
|
||||
files: \.(json|md|ya?ml)$
|
||||
language: node
|
||||
additional_dependencies: ["prettier@3.3.3"]
|
||||
additional_dependencies: ["prettier@3.5.3"]
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.24.0
|
||||
rev: v8.24.3
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
name: detect hardcoded secrets
|
||||
name: run gitleaks
|
||||
description: detect hardcoded secrets with gitleaks
|
||||
- repo: https://github.com/shssoichiro/oxipng
|
||||
rev: v9.1.4
|
||||
hooks:
|
||||
- id: oxipng
|
||||
name: run oxipng
|
||||
description: use lossless compression to optimize PNG files
|
||||
args: ["-o", "4", "--strip", "safe", "--alpha"]
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
@@ -64,10 +70,12 @@ repos:
|
||||
rev: v1.5.5
|
||||
hooks:
|
||||
- id: forbid-tabs
|
||||
name: no-tabs checker
|
||||
name: run no-tabs checker
|
||||
description: check the codebase for tabs
|
||||
exclude: Makefile$
|
||||
- id: remove-tabs
|
||||
name: tabs remover
|
||||
name: run tabs remover
|
||||
description: find and convert tabs to spaces
|
||||
args: [--whitespaces-count, "2"]
|
||||
exclude: Makefile$
|
||||
- repo: https://github.com/rhysd/actionlint
|
||||
@@ -81,7 +89,7 @@ repos:
|
||||
hooks:
|
||||
- id: codespell
|
||||
name: run codespell
|
||||
description: Check spelling with codespell
|
||||
description: check spelling with codespell
|
||||
args: [--ignore-words=.github/linters/codespell.txt]
|
||||
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||
rev: 3.0.0
|
||||
@@ -95,7 +103,7 @@ repos:
|
||||
hooks:
|
||||
- id: markdownlint
|
||||
name: run markdownlint
|
||||
description: Checks the style of Markdown files
|
||||
description: checks the style of Markdown files
|
||||
args: [--config=.github/linters/.markdown-lint.yml]
|
||||
types: [markdown]
|
||||
files: \.md$
|
||||
@@ -104,7 +112,7 @@ repos:
|
||||
hooks:
|
||||
- id: markdown-link-check
|
||||
name: run markdown-link-check
|
||||
description: Checks hyperlinks in Markdown files
|
||||
description: checks hyperlinks in Markdown files
|
||||
args: [--config=.github/linters/mlc_config.json, -q]
|
||||
types: [markdown]
|
||||
files: \.md$
|
||||
@@ -113,12 +121,13 @@ repos:
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
name: run shellcheck
|
||||
description: check shell scripts with a static analysis tool
|
||||
- repo: https://github.com/adrienverge/yamllint
|
||||
rev: v1.36.0
|
||||
rev: v1.37.0
|
||||
hooks:
|
||||
- id: yamllint
|
||||
name: run yamllint
|
||||
description: Check YAML files with yamllint
|
||||
description: check YAML files with yamllint
|
||||
args: [--strict, -c=.github/linters/.yaml-lint.yml]
|
||||
types: [yaml]
|
||||
files: \.ya?ml$
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# Authors of mruby (mruby developers)
|
||||
|
||||
## The List of Contributors sorted by number of commits (as of 2025-03-01 8ba2ed6)
|
||||
## The List of Contributors sorted by number of commits (as of 2025-04-02 07d7aa6)
|
||||
|
||||
6083 Yukihiro "Matz" Matsumoto (@matz)*
|
||||
673 dearblue (@dearblue)*
|
||||
6146 Yukihiro "Matz" Matsumoto (@matz)*
|
||||
676 dearblue (@dearblue)*
|
||||
587 KOBAYASHI Shuji (@shuujii)
|
||||
353 Daniel Bovensiepen (@bovi)*
|
||||
345 Takeshi Watanabe (@take-cheeze)*
|
||||
333 Masaki Muranaka (@monaka)
|
||||
234 Jun Hiroe (@suzukaze)
|
||||
228 Tomoyuki Sahara (@tsahara)*
|
||||
225 John Bampton (@jbampton)
|
||||
220 Cremno (@cremno)*
|
||||
219 John Bampton (@jbampton)
|
||||
209 Yuki Kurihara (@ksss)+
|
||||
144 Yasuhiro Matsumoto (@mattn)*
|
||||
113 Carson McDonald (@carsonmcdonald)
|
||||
@@ -103,6 +103,7 @@
|
||||
5 Chris Reuter (@suetanvil)
|
||||
5 Davide D'Agostino (@DAddYE)
|
||||
5 Eric Hodel (@drbrain)
|
||||
5 HASUMI Hitoshi (@hasumikin)
|
||||
5 Hendrik (@Asmod4n)
|
||||
5 Ichito Nagata (@i110)
|
||||
5 Keita Obo (@ktaobo)*
|
||||
@@ -117,7 +118,6 @@
|
||||
5 xuejianqing (@joans321)
|
||||
4 Dante Catalfamo (@dantecatalfamo)
|
||||
4 Goro Kikuchi (@gorogit)
|
||||
4 HASUMI Hitoshi (@hasumikin)
|
||||
4 Herwin Weststrate (@herwinw)
|
||||
4 Horimoto Yasuhiro (@komainu8)
|
||||
4 Jon Moss (@maclover7)
|
||||
@@ -126,6 +126,7 @@
|
||||
4 Lanza (@LanzaSchneider)
|
||||
4 Li Yazhou (@flaneur2020)
|
||||
4 Marcus Stollsteimer (@stomar)
|
||||
4 Mark Delk (@jethrodaniel)
|
||||
4 NARUSE, Yui (@nurse)
|
||||
4 Ravil Bayramgalin (@brainopia)*+
|
||||
4 Satoshi Odawara (@SatoshiOdawara)
|
||||
@@ -143,7 +144,6 @@
|
||||
3 Jan Berdajs (@mrbrdo)
|
||||
3 Jonas Minnberg (@sasq64)
|
||||
3 Joseph McCullough (@joequery)
|
||||
3 Mark Delk (@jethrodaniel)
|
||||
3 Mark McCurry (@fundamental)
|
||||
3 Nobuhiro Iwamatsu (@iwamatsu)
|
||||
3 Per Lundberg (@perlun)*
|
||||
@@ -303,6 +303,7 @@
|
||||
1 arton (@arton)
|
||||
1 duangsuse (@duangsuse)
|
||||
1 fl0l0u (@fl0l0u)
|
||||
1 Bradley Whited (@esotericpig)
|
||||
1 hhc0null (@hhc0null)
|
||||
1 iTitou (@titouanc)
|
||||
1 javier ramírez (@javier)
|
||||
|
||||
@@ -60,7 +60,7 @@ MRuby::Build.new do |conf|
|
||||
# gperf settings
|
||||
# conf.gperf do |gperf|
|
||||
# gperf.command = 'gperf'
|
||||
# gperf.compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
|
||||
# gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
|
||||
# end
|
||||
|
||||
# file extensions
|
||||
|
||||
@@ -16,7 +16,7 @@ First, `realloc(NULL, size)` behaves the same as malloc(size). Second, `free(NUL
|
||||
|
||||
## Redefine `mrb_default_allocf()`
|
||||
|
||||
The only function in mruby that uses the standard C library's memory allocation functions is `mrb_default_allocf()`, defined in `alloc.c`. By defining this function within your application, you can customize the memory management of your application.
|
||||
The only function in mruby that uses the standard C library's memory allocation functions is `mrb_default_allocf()`, defined in `allocf.c`. By defining this function within your application, you can customize the memory management of your application.
|
||||
|
||||
## Specify a function with `mrb_open_allocf()`
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ header.
|
||||
For `MRB_OPSYM()`, specify the names corresponding to operators (see
|
||||
`MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that
|
||||
can be specified for it). Other than that, describe only word characters
|
||||
excluding leading and ending punctuations.
|
||||
excluding leading and ending punctuation.
|
||||
|
||||
These macros are converted to static symbol IDs at compile time, unless
|
||||
preallocate symbols are disabled by `conf.disable_presym`. In that case,
|
||||
|
||||
+2
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
# The language
|
||||
|
||||
- mruby now supports `private` and `protected` visibitily ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd))
|
||||
- mruby now supports `private` and `protected` visibility ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd))
|
||||
- Maximum length of inlined symbols reduced from 5 to 4 characters to provide space for visibility flags ([6442a01](https://github.com/mruby/mruby/commit/6442a01))
|
||||
- Many methods are made private according to CRuby visibility ([4a0e806](https://github.com/mruby/mruby/commit/4a0e806))
|
||||
- Generate OP_SSEND for `self.method` type calls ([111fe4b](https://github.com/mruby/mruby/commit/111fe4b))
|
||||
@@ -131,7 +131,7 @@
|
||||
- [#6243](https://github.com/mruby/mruby/pull/6243) Fixed base64 decoding in `mruby-pack`
|
||||
- [#6244](https://github.com/mruby/mruby/pull/6244) Revise scope of role of `mrb_vm_run()`
|
||||
- [#6246](https://github.com/mruby/mruby/pull/6246) Fix typo in `test/t/hash.rb`
|
||||
- [#6249](https://github.com/mruby/mruby/pull/6249) Fix grammar in `src/vm.c`; `catched` -> `caught`
|
||||
- [#6249](https://github.com/mruby/mruby/pull/6249) Fix spelling in `src/vm.c`
|
||||
- [#6250](https://github.com/mruby/mruby/pull/6250) Fix spelling
|
||||
- [#6251](https://github.com/mruby/mruby/pull/6251) Clean up root move `CODEOWNERS` to `.github` directory
|
||||
- [#6253](https://github.com/mruby/mruby/pull/6253) Allow recycling fibers by GC if not referenced directly
|
||||
|
||||
@@ -50,8 +50,9 @@ enum mrb_special_consts {
|
||||
#define WORDBOX_FIXNUM_FLAG (1 << (WORDBOX_FIXNUM_BIT_POS - 1))
|
||||
#define WORDBOX_FIXNUM_MASK ((1 << WORDBOX_FIXNUM_BIT_POS) - 1)
|
||||
|
||||
#if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
|
||||
#if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) || defined(MRB_NO_FLOAT)
|
||||
/* floats are allocated in heaps */
|
||||
#define WORDBOX_IMMEDIATE_MASK 0x03
|
||||
#define WORDBOX_SYMBOL_BIT_POS 2
|
||||
#define WORDBOX_SYMBOL_SHIFT WORDBOX_SYMBOL_BIT_POS
|
||||
#define WORDBOX_SYMBOL_FLAG (1 << (WORDBOX_SYMBOL_BIT_POS - 1))
|
||||
@@ -68,7 +69,9 @@ enum mrb_special_consts {
|
||||
#define WORDBOX_SYMBOL_MASK 0x1f
|
||||
#endif
|
||||
|
||||
#ifndef WORDBOX_IMMEDIATE_MASK
|
||||
#define WORDBOX_IMMEDIATE_MASK 0x07
|
||||
#endif
|
||||
|
||||
#define WORDBOX_SET_SHIFT_VALUE(o,n,v) \
|
||||
((o).w = (((uintptr_t)(v)) << WORDBOX_##n##_SHIFT) | WORDBOX_##n##_FLAG)
|
||||
@@ -107,7 +110,7 @@ enum mrb_special_consts {
|
||||
* undef : ...0001 0100
|
||||
* fixnum: ...IIII III1
|
||||
* symbol: ...SSSS SS10
|
||||
* object: ...PPPP P000 (any bits are 1)
|
||||
* object: ...PPPP PP00 (any bits are 1)
|
||||
*/
|
||||
typedef struct mrb_value {
|
||||
uintptr_t w;
|
||||
@@ -145,7 +148,11 @@ MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float);
|
||||
#endif
|
||||
MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int);
|
||||
|
||||
#if WORDBOX_IMMEDIATE_MASK == 0x3
|
||||
#define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w <= MRB_Qundef)
|
||||
#else
|
||||
#define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w == MRB_Qnil)
|
||||
#endif
|
||||
|
||||
#define mrb_ptr(o) mrb_val_union(o).p
|
||||
#define mrb_cptr(o) mrb_val_union(o).vp->p
|
||||
|
||||
@@ -176,11 +176,12 @@ size_t mrb_obj_iv_tbl_memsize(mrb_value);
|
||||
mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*);
|
||||
void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
|
||||
mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod);
|
||||
mrb_value mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary);
|
||||
mrb_value mrb_f_global_variables(mrb_state *mrb, mrb_value self);
|
||||
mrb_value mrb_obj_instance_variables(mrb_state*, mrb_value);
|
||||
mrb_value mrb_mod_class_variables(mrb_state*, mrb_value);
|
||||
mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass * c, mrb_sym sym);
|
||||
mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass * c, mrb_sym sym);
|
||||
mrb_value mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym);
|
||||
mrb_bool mrb_mod_cv_defined(mrb_state *mrb, struct RClass *c, mrb_sym sym);
|
||||
mrb_bool mrb_ident_p(const char *s, mrb_int len);
|
||||
mrb_value mrb_exc_const_get(mrb_state *mrb, mrb_sym sym);
|
||||
|
||||
@@ -247,6 +248,7 @@ void mrb_gc_free_bint(mrb_state *mrb, struct RBasic *x);
|
||||
void mrb_bint_copy(mrb_state *mrb, mrb_value x, mrb_value y);
|
||||
size_t mrb_bint_memsize(mrb_value x);
|
||||
mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x);
|
||||
mrb_value mrb_bint_sqrt(mrb_state *mrb, mrb_value x);
|
||||
#endif
|
||||
|
||||
#endif /* MRUBY_INTERNAL_H */
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* For `MRB_OPSYM`, specify the names corresponding to operators (see
|
||||
* `MRuby::Presym::OPERATORS` in `lib/mruby/presym.rb` for the names that
|
||||
* can be specified for it). Other than that, describe only word characters
|
||||
* excluding leading and ending punctuations.
|
||||
* excluding leading and ending punctuation.
|
||||
*
|
||||
* These macros are expanded to `mrb_intern_lit` if presym is disabled,
|
||||
* therefore the mruby state variable is required. The above macros can be
|
||||
|
||||
@@ -94,12 +94,6 @@ struct mrb_state;
|
||||
# define MRB_PRIx PRIx32
|
||||
#endif
|
||||
|
||||
#ifdef MRB_ENDIAN_BIG
|
||||
# define MRB_ENDIAN_LOHI(a,b) a b
|
||||
#else
|
||||
# define MRB_ENDIAN_LOHI(a,b) b a
|
||||
#endif
|
||||
|
||||
MRB_API mrb_bool mrb_read_int(const char *p, const char *e, char **endp, mrb_int *np);
|
||||
/* obsolete; do not use mrb_int_read() */
|
||||
MRB_API mrb_int mrb_int_read(const char*, const char*, char**);
|
||||
|
||||
@@ -266,7 +266,7 @@ module MRuby
|
||||
def initialize(build)
|
||||
super
|
||||
@command = 'gperf'
|
||||
@compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
|
||||
@compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
|
||||
end
|
||||
|
||||
def run(outfile, infile)
|
||||
|
||||
@@ -882,7 +882,7 @@ mpz_get_str(mrb_state *mrb, char *s, mrb_int sz, mrb_int base, mpz_t *x)
|
||||
if ((base & (base - 1)) == 0) { // base is a power of 2
|
||||
int shift = 0;
|
||||
while ((1 << shift) < base) shift++;
|
||||
mp_limb mask = base - 1;
|
||||
mp_limb mask = (mp_limb)base - 1;
|
||||
mp_dbl_limb value = 0;
|
||||
int bits = 0;
|
||||
|
||||
@@ -1246,6 +1246,61 @@ mpz_gcd(mrb_state *mrb, mpz_t *gg, mpz_t *aa, mpz_t *bb)
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t
|
||||
mpz_bits(const mpz_t *x)
|
||||
{
|
||||
if (x->sz == 0 || x->sn == 0) return 0;
|
||||
|
||||
size_t limb_bits = sizeof(mp_limb) * 8;
|
||||
|
||||
// Get the most significant limb
|
||||
size_t i = x->sz - 1;
|
||||
mp_limb high = x->p[i];
|
||||
|
||||
// Number of bits = total full limbs + significant bits in top limb
|
||||
return i * limb_bits + (limb_bits - lzb(high));
|
||||
}
|
||||
|
||||
static void
|
||||
mpz_sqrt(mrb_state *mrb, mpz_t *z, mpz_t *x)
|
||||
{
|
||||
mrb_assert(x->sn >= 0);
|
||||
|
||||
if (x->sz == 0) {
|
||||
// sqrt(0) = 0
|
||||
z->sn = 0;
|
||||
z->sz = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Estimate initial value: 1 << (bit_length(x) / 2)
|
||||
size_t xbits = mpz_bits(x);
|
||||
size_t sbit = (xbits + 1) / 2;
|
||||
mpz_t s, t;
|
||||
mpz_init_set_int(mrb, &s, 1);
|
||||
mpz_mul_2exp(mrb, &s, &s, sbit);
|
||||
|
||||
mpz_init(mrb, &t);
|
||||
|
||||
// Iteratively refine s using Newton-Raphson method:
|
||||
// s = (s + x / s) / 2
|
||||
for (;;) {
|
||||
mpz_mdiv(mrb, &t, x, &s); // t = x / s
|
||||
mpz_add(mrb, &t, &t, &s); // t = s + x/s
|
||||
mpz_div_2exp(mrb, &t, &t, 1); // t = (s + x/s) / 2
|
||||
|
||||
if (mpz_cmp(mrb, &t, &s) >= 0) {
|
||||
// Converged: t >= s
|
||||
break;
|
||||
}
|
||||
|
||||
mpz_set(mrb, &s, &t);
|
||||
}
|
||||
|
||||
mpz_move(mrb, z, &s);
|
||||
mpz_clear(mrb, &t);
|
||||
}
|
||||
|
||||
/* --- mruby functions --- */
|
||||
/* initialize mpz_t from RBigint (not need to clear) */
|
||||
static void
|
||||
@@ -1861,7 +1916,7 @@ mrb_bint_to_s(mrb_state *mrb, mrb_value x, mrb_int base)
|
||||
return mrb_str_new_lit(mrb, "0");
|
||||
}
|
||||
size_t len = mpz_sizeinbase(&a, (int)base);
|
||||
if (MRB_INT_MAX-2 < len) {
|
||||
if (sizeof(size_t) >= sizeof(mrb_int) && MRB_INT_MAX-2 < len) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "too long string from Integer");
|
||||
}
|
||||
mrb_value str = mrb_str_new(mrb, NULL, len+2);
|
||||
@@ -2014,6 +2069,23 @@ mrb_bint_memsize(mrb_value x)
|
||||
return z.sz * sizeof(mp_limb);
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_bint_sqrt(mrb_state *mrb, mrb_value x)
|
||||
{
|
||||
mpz_t a;
|
||||
|
||||
bint_as_mpz(RBIGINT(x), &a);
|
||||
if (a.sn < 0) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "square root of negative number");
|
||||
}
|
||||
|
||||
mpz_t z;
|
||||
mpz_init(mrb, &z);
|
||||
mpz_sqrt(mrb, &z, &a);
|
||||
|
||||
return bint_norm(mrb, bint_new(mrb, &z));
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_bint_hash(mrb_state *mrb, mrb_value x)
|
||||
{
|
||||
|
||||
@@ -811,7 +811,7 @@ realloc_pool_str(codegen_scope *s, mrb_irep_pool *p, mrb_int len)
|
||||
str = (char*)p->u.str;
|
||||
str = (char*)codegen_realloc(s, str, len+1);
|
||||
}
|
||||
p->tt = len<<2 | IREP_TT_STR;
|
||||
p->tt = (uint32_t)(len<<2 | IREP_TT_STR);
|
||||
str[len] = '\0';
|
||||
p->u.str = (const char*)str;
|
||||
}
|
||||
@@ -4012,7 +4012,7 @@ scope_new(mrb_state *mrb, codegen_scope *prev, node *nlv)
|
||||
|
||||
s->lv = nlv;
|
||||
s->sp += node_len(nlv)+1; /* add self */
|
||||
s->nlocals = s->sp;
|
||||
s->nlocals = s->nregs = s->sp;
|
||||
if (nlv) {
|
||||
mrb_sym *lv;
|
||||
node *n = nlv;
|
||||
|
||||
@@ -1540,7 +1540,7 @@ prohibit_literals(parser_state *p, node *n)
|
||||
modifier_while "'while' modifier"
|
||||
modifier_until "'until' modifier"
|
||||
modifier_rescue "'rescue' modifier"
|
||||
keyword_alias "'alis'"
|
||||
keyword_alias "'alias'"
|
||||
keyword_BEGIN "'BEGIN'"
|
||||
keyword_END "'END'"
|
||||
keyword__LINE__ "'__LINE__'"
|
||||
|
||||
@@ -820,8 +820,8 @@ module Enumerable
|
||||
return to_enum(:filter_map) unless blk
|
||||
|
||||
ary = []
|
||||
self.each do |x|
|
||||
x = blk.call(x)
|
||||
self.each do |*x|
|
||||
x = blk.call(*x)
|
||||
ary.push x if x
|
||||
end
|
||||
ary
|
||||
@@ -851,7 +851,8 @@ module Enumerable
|
||||
# ["a", "b", "c", "b"].tally #=> {"a"=>1, "b"=>2, "c"=>1}
|
||||
def tally
|
||||
hash = {}
|
||||
self.each do |x|
|
||||
self.each do |*x|
|
||||
x = x.__svalue
|
||||
hash[x] = (hash[x]||0)+1
|
||||
end
|
||||
hash
|
||||
@@ -870,12 +871,12 @@ module Enumerable
|
||||
def sum(init=0,&block)
|
||||
result=init
|
||||
if block
|
||||
self.each do |e|
|
||||
result += block.call(e)
|
||||
self.each do |*e|
|
||||
result += block.call(*e)
|
||||
end
|
||||
else
|
||||
self.each do |e|
|
||||
result += e
|
||||
self.each do |*e|
|
||||
result += e.__svalue
|
||||
end
|
||||
end
|
||||
result
|
||||
|
||||
@@ -21,7 +21,7 @@ fiber_s_yield_by_c_method(mrb_state *mrb, mrb_value self)
|
||||
static mrb_value
|
||||
fiber_resume_by_c_func(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
int ci_index = mrb->c->ci - mrb->c->cibase;
|
||||
ptrdiff_t ci_index = mrb->c->ci - mrb->c->cibase;
|
||||
mrb_value ret = mrb_fiber_resume(mrb, self, 0, NULL);
|
||||
if (ci_index != mrb->c->ci - mrb->c->cibase) {
|
||||
mrb_raisef(mrb, E_EXCEPTION,
|
||||
@@ -34,7 +34,7 @@ fiber_resume_by_c_func(mrb_state *mrb, mrb_value self)
|
||||
static mrb_value
|
||||
fiber_resume_by_c_method(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
int ci_index = mrb->c->ci - mrb->c->cibase;
|
||||
ptrdiff_t ci_index = mrb->c->ci - mrb->c->cibase;
|
||||
mrb_value ret = mrb_funcall_argv(mrb, self, mrb_intern_lit(mrb, "resume"), 0, NULL);
|
||||
if (ci_index != mrb->c->ci - mrb->c->cibase) {
|
||||
mrb_raisef(mrb, E_EXCEPTION,
|
||||
|
||||
+30
-31
@@ -245,11 +245,9 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass)
|
||||
#if defined(_WIN32)
|
||||
char bname[_MAX_DIR];
|
||||
char extname[_MAX_EXT];
|
||||
char buffer[_MAX_DIR + _MAX_EXT];
|
||||
mrb_value s;
|
||||
char *path;
|
||||
|
||||
mrb_get_args(mrb, "S", &s);
|
||||
char *path = mrb_str_to_cstr(mrb, s);
|
||||
mrb_get_args(mrb, "z", &path);
|
||||
size_t ridx = strlen(path);
|
||||
if (ridx > 0) {
|
||||
ridx--;
|
||||
@@ -257,22 +255,22 @@ mrb_file_basename(mrb_state *mrb, mrb_value klass)
|
||||
path[ridx] = '\0';
|
||||
ridx--;
|
||||
}
|
||||
if (strncmp(path, "/", 2) == 0) {
|
||||
if (ridx == 0 && path[0] == '/') {
|
||||
return mrb_str_new_cstr(mrb, path);
|
||||
}
|
||||
}
|
||||
_splitpath((const char*)path, NULL, NULL, bname, extname);
|
||||
snprintf(buffer, _MAX_DIR + _MAX_EXT, "%s%s", bname, extname);
|
||||
return mrb_str_new_cstr(mrb, buffer);
|
||||
mrb_value buffer = mrb_str_new_cstr(mrb, bname);
|
||||
mrb_str_cat_cstr(mrb, buffer, extname);
|
||||
return buffer;
|
||||
#else
|
||||
mrb_value s;
|
||||
mrb_get_args(mrb, "S", &s);
|
||||
char *path = mrb_str_to_cstr(mrb, s);
|
||||
char *bname;
|
||||
char *path, *bname;
|
||||
|
||||
mrb_get_args(mrb, "z", &path);
|
||||
if ((bname = basename(path)) == NULL) {
|
||||
mrb_sys_fail(mrb, "basename");
|
||||
}
|
||||
if (strncmp(bname, "//", 3) == 0) bname[1] = '\0'; /* patch for Cygwin */
|
||||
if (strcmp(bname, "//") == 0) bname[1] = '\0'; /* patch for Cygwin */
|
||||
return mrb_str_new_cstr(mrb, bname);
|
||||
#endif
|
||||
}
|
||||
@@ -281,10 +279,10 @@ static mrb_value
|
||||
mrb_file_realpath(mrb_state *mrb, mrb_value klass)
|
||||
{
|
||||
mrb_value pathname, dir_string;
|
||||
mrb_int argc = mrb_get_args(mrb, "S|S", &pathname, &dir_string);
|
||||
if (argc == 2) {
|
||||
|
||||
if (mrb_get_args(mrb, "S|S", &pathname, &dir_string) == 2) {
|
||||
mrb_value s = mrb_str_dup(mrb, dir_string);
|
||||
s = mrb_str_append(mrb, s, mrb_str_new_cstr(mrb, FILE_SEPARATOR));
|
||||
s = mrb_str_cat_cstr(mrb, s, FILE_SEPARATOR);
|
||||
s = mrb_str_append(mrb, s, pathname);
|
||||
pathname = s;
|
||||
}
|
||||
@@ -363,7 +361,7 @@ path_parse(mrb_state *mrb, mrb_value ary, const char *path, int ai)
|
||||
SKIP_DIRSEP(path);
|
||||
const char *path0 = path;
|
||||
NEXT_DIRSEP(path);
|
||||
int len = path - path0;
|
||||
ptrdiff_t len = path - path0;
|
||||
if (len == 0) {
|
||||
break;
|
||||
}
|
||||
@@ -414,7 +412,7 @@ path_gethome(mrb_state *mrb, const char **pathp)
|
||||
|
||||
const char *username = ++*pathp;
|
||||
NEXT_DIRSEP(*pathp);
|
||||
int len = *pathp - username;
|
||||
ptrdiff_t len = *pathp - username;
|
||||
|
||||
if (len == 0) {
|
||||
home = getenv("HOME");
|
||||
@@ -431,7 +429,7 @@ path_gethome(mrb_state *mrb, const char **pathp)
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *uname = RSTRING_CSTR(mrb, mrb_str_new(mrb, username, len));
|
||||
const char *uname = RSTRING_CSTR(mrb, mrb_str_new(mrb, username, (mrb_int)len));
|
||||
#if defined(_WIN32) || defined(MRB_IO_NO_PWNAM)
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "user %s doesn't exist", uname);
|
||||
#else
|
||||
@@ -458,7 +456,7 @@ path_expand(mrb_state *mrb, const char *path, const char *base, mrb_bool tilda)
|
||||
{
|
||||
mrb_value ary;
|
||||
|
||||
// split path conponents as array and normalization
|
||||
// split path components as array and normalization
|
||||
if (tilda && path[0] == '~') {
|
||||
base = path_gethome(mrb, &path);
|
||||
ary = path_split(mrb, path, base, NULL);
|
||||
@@ -639,7 +637,7 @@ mrb_file_flock(mrb_state *mrb, mrb_value self)
|
||||
}
|
||||
/* FALLTHRU - should not happen */
|
||||
default:
|
||||
mrb_sys_fail(mrb, "flock failed");
|
||||
mrb_sys_fail(mrb, "flock");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -653,10 +651,10 @@ mrb_file_size(mrb_state *mrb, mrb_value self)
|
||||
mrb_stat st;
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
if (mrb_fstat(fd, &st) == -1) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "fstat failed");
|
||||
mrb_sys_fail(mrb, "fstat");
|
||||
}
|
||||
|
||||
if (st.st_size > MRB_INT_MAX) {
|
||||
if (sizeof(st.st_size) >= sizeof(mrb_int) && st.st_size > MRB_INT_MAX) {
|
||||
#ifdef MRB_NO_FLOAT
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "File#size too large for MRB_NO_FLOAT");
|
||||
#else
|
||||
@@ -702,7 +700,7 @@ mrb_file_truncate(mrb_state *mrb, mrb_value self)
|
||||
int fd = mrb_io_fileno(mrb, self);
|
||||
mrb_int length = mrb_as_int(mrb, lenv);
|
||||
if (mrb_ftruncate(fd, length) != 0) {
|
||||
mrb_raise(mrb, E_IO_ERROR, "ftruncate failed");
|
||||
mrb_sys_fail(mrb, "ftruncate");
|
||||
}
|
||||
|
||||
return mrb_fixnum_value(0);
|
||||
@@ -740,6 +738,7 @@ mrb_file_s_chmod(mrb_state *mrb, mrb_value klass)
|
||||
|
||||
mrb_get_args(mrb, "i*", &mode, &filenames, &argc);
|
||||
for (int i = 0; i < argc; i++) {
|
||||
mrb_ensure_string_type(mrb, filenames[i]);
|
||||
const char *utf8_path = RSTRING_CSTR(mrb, filenames[i]);
|
||||
char *path = mrb_locale_from_utf8(utf8_path, -1);
|
||||
if (CHMOD(path, mode) == -1) {
|
||||
@@ -761,17 +760,16 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass)
|
||||
return mrb_nil_value(); // unreachable
|
||||
#else
|
||||
const char *path;
|
||||
char *buf, *tmp;
|
||||
size_t bufsize = 100;
|
||||
ssize_t rc;
|
||||
mrb_value ret;
|
||||
|
||||
mrb_get_args(mrb, "z", &path);
|
||||
tmp = mrb_locale_from_utf8(path, -1);
|
||||
|
||||
buf = (char*)mrb_malloc(mrb, bufsize);
|
||||
while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize && rc != -1) {
|
||||
bufsize *= 2;
|
||||
char *tmp = mrb_locale_from_utf8(path, -1);
|
||||
char *buf = (char*)mrb_malloc(mrb, bufsize);
|
||||
|
||||
ssize_t rc;
|
||||
while ((rc = readlink(tmp, buf, bufsize)) == (ssize_t)bufsize) {
|
||||
bufsize += 100;
|
||||
buf = (char*)mrb_realloc(mrb, buf, bufsize);
|
||||
}
|
||||
mrb_locale_free(tmp);
|
||||
@@ -780,7 +778,8 @@ mrb_file_s_readlink(mrb_state *mrb, mrb_value klass)
|
||||
mrb_sys_fail(mrb, path);
|
||||
}
|
||||
tmp = mrb_utf8_from_locale(buf, -1);
|
||||
ret = mrb_str_new(mrb, tmp, rc);
|
||||
|
||||
mrb_value ret = mrb_str_new(mrb, tmp, rc);
|
||||
mrb_utf8_free(tmp);
|
||||
mrb_free(mrb, buf);
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ mrb_mruby_io_gem_test(mrb_state* mrb)
|
||||
}
|
||||
#endif
|
||||
if (env_home) {
|
||||
char *utf8 = mrb_utf8_from_locale(env_home, strlen(env_home));
|
||||
char *utf8 = mrb_utf8_from_locale(env_home, -1);
|
||||
mrb_value path = mrb_str_new_cstr(mrb, utf8);
|
||||
#ifdef _WIN32
|
||||
char *pathp = RSTRING_PTR(path);
|
||||
|
||||
@@ -643,8 +643,29 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
|
||||
static mrb_value
|
||||
mrb_mod_s_constants(mrb_state *mrb, mrb_value mod)
|
||||
{
|
||||
mrb_raise(mrb, E_NOTIMP_ERROR, "Module.constants not implemented");
|
||||
return mrb_nil_value(); /* not reached */
|
||||
if (mrb_get_argc(mrb) > 0 || mrb_class_ptr(mod) != mrb->module_class) {
|
||||
return mrb_mod_constants(mrb, mod);
|
||||
}
|
||||
|
||||
const struct RProc *proc = mrb->c->ci[-1].proc;
|
||||
struct RClass *c = MRB_PROC_TARGET_CLASS(proc);
|
||||
mrb_value ary = mrb_ary_new(mrb);
|
||||
|
||||
if (!c) c = mrb->object_class;
|
||||
mrb_mod_const_at(mrb, c, ary);
|
||||
proc = proc->upper;
|
||||
while (proc) {
|
||||
struct RClass *c2 = MRB_PROC_TARGET_CLASS(proc);
|
||||
if (!c2) c2 = mrb->object_class;
|
||||
mrb_mod_const_at(mrb, c2, ary);
|
||||
proc = proc->upper;
|
||||
}
|
||||
while (c) {
|
||||
mrb_mod_const_at(mrb, c, ary);
|
||||
c = c->super;
|
||||
if (c == mrb->object_class) break;
|
||||
}
|
||||
return ary;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
|
||||
@@ -262,6 +262,46 @@ flo_remainder(mrb_state *mrb, mrb_value self)
|
||||
}
|
||||
#endif
|
||||
|
||||
static mrb_int
|
||||
isqrt(mrb_int n)
|
||||
{
|
||||
mrb_assert(n >= 0);
|
||||
if (n < 2) return n;
|
||||
|
||||
mrb_int x = n;
|
||||
mrb_int y = (x + 1) / 2;
|
||||
|
||||
// Babylonian method (integer version)
|
||||
while (y < x) {
|
||||
x = y;
|
||||
y = (x + n / x) / 2;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
int_sqrt(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_value arg = mrb_get_arg1(mrb);
|
||||
|
||||
if (mrb_integer_p(arg)) {
|
||||
mrb_int n = mrb_integer(arg);
|
||||
if (n < 0) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "non-negative integer required");
|
||||
}
|
||||
return mrb_int_value(mrb, isqrt(n));
|
||||
}
|
||||
#ifdef MRB_USE_BIGINT
|
||||
else if (mrb_bigint_p(arg)) {
|
||||
return mrb_bint_sqrt(mrb, arg);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
mrb_raise(mrb, E_TYPE_ERROR, "expected Integer");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
|
||||
{
|
||||
@@ -275,6 +315,7 @@ mrb_mruby_numeric_ext_gem_init(mrb_state* mrb)
|
||||
mrb_define_method_id(mrb, ic, MRB_SYM(size), int_size, MRB_ARGS_NONE());
|
||||
mrb_define_method_id(mrb, ic, MRB_SYM_Q(odd), int_odd, MRB_ARGS_NONE());
|
||||
mrb_define_method_id(mrb, ic, MRB_SYM_Q(even), int_even, MRB_ARGS_NONE());
|
||||
mrb_define_class_method_id(mrb, ic, MRB_SYM(sqrt), int_sqrt, MRB_ARGS_REQ(1));
|
||||
|
||||
#ifndef MRB_NO_FLOAT
|
||||
struct RClass *fc = mrb->float_class;
|
||||
|
||||
@@ -81,3 +81,9 @@ assert('Integer#digits') do
|
||||
assert_equal([4, 6, 6, 0, 5], 12345.digits(7))
|
||||
assert_equal([45, 23, 1], 12345.digits(100))
|
||||
end
|
||||
|
||||
assert('Integer.sqrt') do
|
||||
assert_equal(4, Integer.sqrt(16))
|
||||
assert_equal(10, Integer.sqrt(100))
|
||||
assert_equal(85, Integer.sqrt(7244))
|
||||
end
|
||||
|
||||
@@ -1188,7 +1188,7 @@ pack_nul(mrb_state *mrb, mrb_value dst, mrb_int didx, int count)
|
||||
}
|
||||
|
||||
static void
|
||||
check_x(mrb_state *mrb, int a, int count, char c)
|
||||
check_x(mrb_state *mrb, mrb_int a, mrb_int count, char c)
|
||||
{
|
||||
if (a < count) {
|
||||
mrb_raisef(mrb, E_ARGUMENT_ERROR, "%c outside of string", c);
|
||||
@@ -1209,7 +1209,7 @@ has_tmpl(const struct tmpl *tmpl)
|
||||
}
|
||||
|
||||
static enum pack_dir
|
||||
read_tmpl(mrb_state *mrb, struct tmpl *tmpl, enum pack_type *typep, int *sizep, int *countp, unsigned int *flagsp)
|
||||
read_tmpl(mrb_state *mrb, struct tmpl *tmpl, enum pack_type *typep, mrb_int *sizep, mrb_int *countp, unsigned int *flagsp)
|
||||
{
|
||||
mrb_int t, tlen;
|
||||
int ch, size = 0;
|
||||
@@ -1505,7 +1505,7 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
|
||||
mrb_value o, result;
|
||||
struct tmpl tmpl;
|
||||
enum pack_type type;
|
||||
int count, size;
|
||||
mrb_int count, size;
|
||||
unsigned int flags;
|
||||
enum pack_dir dir;
|
||||
|
||||
@@ -1627,16 +1627,15 @@ mrb_pack_pack(mrb_state *mrb, mrb_value ary)
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
pack_unpack(mrb_state *mrb, mrb_value str, int single)
|
||||
pack_unpack(mrb_state *mrb, mrb_value str, mrb_bool single)
|
||||
{
|
||||
mrb_value result;
|
||||
struct tmpl tmpl;
|
||||
int count;
|
||||
mrb_int count;
|
||||
unsigned int flags;
|
||||
enum pack_dir dir;
|
||||
enum pack_type type;
|
||||
int size;
|
||||
int srcidx, srclen;
|
||||
mrb_int size, srcidx, srclen;
|
||||
const unsigned char *sptr;
|
||||
|
||||
prepare_tmpl(mrb, &tmpl);
|
||||
@@ -1749,13 +1748,13 @@ pack_unpack(mrb_state *mrb, mrb_value str, int single)
|
||||
static mrb_value
|
||||
mrb_pack_unpack(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
return pack_unpack(mrb, str, 0);
|
||||
return pack_unpack(mrb, str, FALSE);
|
||||
}
|
||||
|
||||
static mrb_value
|
||||
mrb_pack_unpack1(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
return pack_unpack(mrb, str, 1);
|
||||
return pack_unpack(mrb, str, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -1028,9 +1028,9 @@ rational_hash(mrb_state *mrb, mrb_value rat)
|
||||
#ifdef RAT_BIGINT
|
||||
if (RAT_BIGINT_P(rat)) {
|
||||
mrb_value tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.num));
|
||||
hash = mrb_integer(tmp);
|
||||
hash = (uint32_t)mrb_integer(tmp);
|
||||
tmp = mrb_bint_hash(mrb, mrb_obj_value(r->b.den));
|
||||
hash ^= mrb_integer(tmp);
|
||||
hash ^= (uint32_t)mrb_integer(tmp);
|
||||
return mrb_int_value(mrb, hash);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -608,7 +608,7 @@ retry:
|
||||
}
|
||||
mrb_value str = mrb_bint_to_s(mrb, val, base);
|
||||
s = RSTRING_PTR(str);
|
||||
len = RSTRING_LEN(str);
|
||||
len = (int)RSTRING_LEN(str);
|
||||
}
|
||||
goto str_skip;
|
||||
#endif
|
||||
|
||||
@@ -909,8 +909,8 @@ int_chr(mrb_state *mrb, mrb_value num)
|
||||
*
|
||||
* Returns next sequence of the string;
|
||||
*
|
||||
* a = "abc"
|
||||
* a.succ #=> "abd"
|
||||
* a = "bed"
|
||||
* a.succ #=> "bee"
|
||||
*/
|
||||
static mrb_value
|
||||
str_succ_bang(mrb_state *mrb, mrb_value self)
|
||||
|
||||
+1
-1
@@ -1407,7 +1407,7 @@ mrb_ary_to_s(mrb_state *mrb, mrb_value self)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check array equality: 1=equal,0=not_equal,-1=need_elments_check */
|
||||
/* check array equality: 1=equal,0=not_equal,-1=need_elements_check */
|
||||
static mrb_int
|
||||
ary_eq(mrb_state *mrb, mrb_value ary1, mrb_value ary2)
|
||||
{
|
||||
|
||||
+1
-1
@@ -2789,7 +2789,7 @@ mrb_mod_module_function(mrb_state *mrb, mrb_value mod)
|
||||
mrb_method_t m = mrb_method_search(mrb, rclass, mid);
|
||||
|
||||
prepare_singleton_class(mrb, (struct RBasic*)rclass);
|
||||
MRB_METHOD_SET_VISIBILITY(m, MT_PRIVATE);
|
||||
MRB_METHOD_SET_VISIBILITY(m, MT_PUBLIC);
|
||||
mrb_define_method_raw(mrb, rclass->c, mid, m);
|
||||
mrb_gc_arena_restore(mrb, ai);
|
||||
}
|
||||
|
||||
+5
-3
@@ -1274,12 +1274,13 @@ mrb_hash_set(mrb_state *mrb, mrb_value hash, mrb_value key, mrb_value val)
|
||||
mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), key);
|
||||
mrb_field_write_barrier_value(mrb, mrb_basic_ptr(hash), val);
|
||||
}
|
||||
|
||||
static void
|
||||
hash_set_default_proc(mrb_state *mrb, mrb_value hash, mrb_value proc)
|
||||
{
|
||||
struct RProc *p = mrb_proc_ptr(proc);
|
||||
if (MRB_PROC_STRICT_P(p)) {
|
||||
int n = mrb_proc_arity(p);
|
||||
mrb_int n = mrb_proc_arity(p);
|
||||
if (n != 2 && (n >= 0 || n < -3)) {
|
||||
if (n < 0) n = -n-1;
|
||||
mrb_raisef(mrb, E_TYPE_ERROR, "default_proc takes two arguments (2 for %d)", n);
|
||||
@@ -1493,11 +1494,12 @@ mrb_hash_set_default_proc(mrb_state *mrb, mrb_value hash)
|
||||
mrb_value ifnone = mrb_get_arg1(mrb);
|
||||
|
||||
hash_modify(mrb, hash);
|
||||
if (!mrb_nil_p(ifnone)) {
|
||||
mrb_bool has_ifnone = !mrb_nil_p(ifnone);
|
||||
if (has_ifnone) {
|
||||
mrb_check_type(mrb, ifnone, MRB_TT_PROC);
|
||||
}
|
||||
mrb_iv_set(mrb, hash, MRB_SYM(ifnone), ifnone);
|
||||
if (!mrb_nil_p(ifnone)) {
|
||||
if (has_ifnone) {
|
||||
hash_set_default_proc(mrb, hash, ifnone);
|
||||
}
|
||||
else {
|
||||
|
||||
+21
-18
@@ -1181,32 +1181,32 @@ enum str_convert_range {
|
||||
};
|
||||
|
||||
static enum str_convert_range
|
||||
str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_int *beg, mrb_int *len)
|
||||
str_convert_range(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_int *beg, mrb_int *len)
|
||||
{
|
||||
if (!mrb_undef_p(alen)) {
|
||||
*beg = mrb_as_int(mrb, indx);
|
||||
*beg = mrb_as_int(mrb, idx);
|
||||
*len = mrb_as_int(mrb, alen);
|
||||
return STR_CHAR_RANGE;
|
||||
}
|
||||
else {
|
||||
switch (mrb_type(indx)) {
|
||||
switch (mrb_type(idx)) {
|
||||
default:
|
||||
indx = mrb_ensure_int_type(mrb, indx);
|
||||
idx = mrb_ensure_int_type(mrb, idx);
|
||||
/* fall through */
|
||||
case MRB_TT_INTEGER:
|
||||
*beg = mrb_integer(indx);
|
||||
*beg = mrb_integer(idx);
|
||||
*len = 1;
|
||||
return STR_CHAR_RANGE;
|
||||
|
||||
case MRB_TT_STRING:
|
||||
*beg = str_index_str(mrb, str, indx, 0);
|
||||
*beg = str_index_str(mrb, str, idx, 0);
|
||||
if (*beg < 0) { break; }
|
||||
*len = RSTRING_LEN(indx);
|
||||
*len = RSTRING_LEN(idx);
|
||||
return STR_BYTE_RANGE_CORRECTED;
|
||||
|
||||
case MRB_TT_RANGE:
|
||||
*len = RSTRING_CHAR_LEN(str);
|
||||
switch (mrb_range_beg_len(mrb, indx, beg, len, *len, TRUE)) {
|
||||
switch (mrb_range_beg_len(mrb, idx, beg, len, *len, TRUE)) {
|
||||
case MRB_RANGE_OK:
|
||||
return STR_CHAR_RANGE_CORRECTED;
|
||||
case MRB_RANGE_OUT:
|
||||
@@ -1220,11 +1220,11 @@ str_convert_range(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen,
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen)
|
||||
mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen)
|
||||
{
|
||||
mrb_int beg, len;
|
||||
|
||||
switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
|
||||
switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) {
|
||||
case STR_CHAR_RANGE_CORRECTED:
|
||||
return str_subseq(mrb, str, beg, len);
|
||||
case STR_CHAR_RANGE:
|
||||
@@ -1232,8 +1232,8 @@ mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen)
|
||||
if (mrb_undef_p(alen) && !mrb_nil_p(str) && RSTRING_LEN(str) == 0) return mrb_nil_value();
|
||||
return str;
|
||||
case STR_BYTE_RANGE_CORRECTED:
|
||||
if (mrb_string_p(indx)) {
|
||||
return mrb_str_dup(mrb, indx);
|
||||
if (mrb_string_p(idx)) {
|
||||
return mrb_str_dup(mrb, idx);
|
||||
}
|
||||
else {
|
||||
return mrb_str_byte_subseq(mrb, str, beg, len);
|
||||
@@ -1417,12 +1417,12 @@ str_escape(mrb_state *mrb, mrb_value str, mrb_bool inspect)
|
||||
}
|
||||
|
||||
static void
|
||||
mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_value replace)
|
||||
mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value alen, mrb_value replace)
|
||||
{
|
||||
mrb_int beg, len, charlen;
|
||||
|
||||
mrb_ensure_string_type(mrb, replace);
|
||||
switch (str_convert_range(mrb, str, indx, alen, &beg, &len)) {
|
||||
switch (str_convert_range(mrb, str, idx, alen, &beg, &len)) {
|
||||
case STR_OUT_OF_RANGE:
|
||||
default:
|
||||
mrb_raise(mrb, E_INDEX_ERROR, "string not matched");
|
||||
@@ -1432,7 +1432,7 @@ mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_
|
||||
}
|
||||
charlen = RSTRING_CHAR_LEN(str);
|
||||
if (beg < 0) { beg += charlen; }
|
||||
if (beg < 0 || beg > charlen) { str_out_of_index(mrb, indx); }
|
||||
if (beg < 0 || beg > charlen) { str_out_of_index(mrb, idx); }
|
||||
/* fall through */
|
||||
case STR_CHAR_RANGE_CORRECTED:
|
||||
beg = chars2bytes(str, 0, beg);
|
||||
@@ -1460,9 +1460,9 @@ mrb_str_aset(mrb_state *mrb, mrb_value str, mrb_value indx, mrb_value alen, mrb_
|
||||
static mrb_value
|
||||
mrb_str_aset_m(mrb_state *mrb, mrb_value str)
|
||||
{
|
||||
mrb_value indx, alen, replace;
|
||||
mrb_value idx, alen, replace;
|
||||
|
||||
switch (mrb_get_args(mrb, "oo|S!", &indx, &alen, &replace)) {
|
||||
switch (mrb_get_args(mrb, "oo|S!", &idx, &alen, &replace)) {
|
||||
case 2:
|
||||
replace = alen;
|
||||
alen = mrb_undef_value();
|
||||
@@ -1470,7 +1470,7 @@ mrb_str_aset_m(mrb_state *mrb, mrb_value str)
|
||||
case 3:
|
||||
break;
|
||||
}
|
||||
mrb_str_aset(mrb, str, indx, alen, replace);
|
||||
mrb_str_aset(mrb, str, idx, alen, replace);
|
||||
return replace;
|
||||
}
|
||||
|
||||
@@ -3046,6 +3046,9 @@ sub_replace(mrb_state *mrb, mrb_value self)
|
||||
mrb_value result;
|
||||
|
||||
mrb_get_args(mrb, "ssi", &p, &plen, &match, &mlen, &found);
|
||||
if (found < 0 || RSTRING_LEN(self) < found) {
|
||||
mrb_raise(mrb, E_RUNTIME_ERROR, "argument out of range");
|
||||
}
|
||||
result = mrb_str_new(mrb, 0, 0);
|
||||
for (mrb_int i=0; i<plen; i++) {
|
||||
if (p[i] != '\\' || i+1==plen) {
|
||||
|
||||
+30
-23
@@ -624,7 +624,7 @@ mrb_mod_cv_get(mrb_state *mrb, struct RClass *c, mrb_sym sym)
|
||||
c = c->super;
|
||||
}
|
||||
if (given) return v;
|
||||
if (cls && cls->tt == MRB_TT_SCLASS) {
|
||||
if (cls->tt == MRB_TT_SCLASS) {
|
||||
mrb_value klass;
|
||||
|
||||
klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
|
||||
@@ -669,7 +669,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
|
||||
c = c->super;
|
||||
}
|
||||
|
||||
if (cls && cls->tt == MRB_TT_SCLASS) {
|
||||
if (cls->tt == MRB_TT_SCLASS) {
|
||||
mrb_value klass;
|
||||
|
||||
klass = mrb_obj_iv_get(mrb, (struct RObject*)cls, MRB_SYM(__attached__));
|
||||
@@ -684,7 +684,7 @@ mrb_mod_cv_set(mrb_state *mrb, struct RClass *c, mrb_sym sym, mrb_value v)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (cls && cls->tt == MRB_TT_ICLASS) {
|
||||
else if (cls->tt == MRB_TT_ICLASS) {
|
||||
c = cls->c;
|
||||
}
|
||||
else {
|
||||
@@ -825,35 +825,35 @@ mrb_const_get(mrb_state *mrb, mrb_value mod, mrb_sym sym)
|
||||
mrb_value
|
||||
mrb_vm_const_get(mrb_state *mrb, mrb_sym sym)
|
||||
{
|
||||
struct RClass *c;
|
||||
struct RClass *c2;
|
||||
mrb_value v;
|
||||
const struct RProc *proc = mrb->c->ci->proc;
|
||||
struct RClass *c = MRB_PROC_TARGET_CLASS(proc), *c2;
|
||||
mrb_value v;
|
||||
|
||||
c = MRB_PROC_TARGET_CLASS(proc);
|
||||
if (!c) c = mrb->object_class;
|
||||
if (iv_get(mrb, class_iv_ptr(c), sym, &v)) {
|
||||
return v;
|
||||
}
|
||||
c2 = c;
|
||||
while (c2 && c2->tt == MRB_TT_SCLASS) {
|
||||
mrb_value klass;
|
||||
|
||||
if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) {
|
||||
c2 = NULL;
|
||||
break;
|
||||
}
|
||||
c2 = mrb_class_ptr(klass);
|
||||
}
|
||||
if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
|
||||
proc = proc->upper;
|
||||
while (proc) {
|
||||
for (proc = proc->upper; proc; proc = proc->upper) {
|
||||
c2 = MRB_PROC_TARGET_CLASS(proc);
|
||||
if (!c2) c2 = mrb->object_class;
|
||||
if (c2 && iv_get(mrb, class_iv_ptr(c2), sym, &v)) {
|
||||
if (iv_get(mrb, class_iv_ptr(c2), sym, &v)) {
|
||||
return v;
|
||||
}
|
||||
proc = proc->upper;
|
||||
}
|
||||
if (c->tt == MRB_TT_SCLASS) {
|
||||
v = const_get_nohook(mrb, c, sym, TRUE);
|
||||
if (!mrb_undef_p(v)) {
|
||||
return v;
|
||||
}
|
||||
|
||||
mrb_value klass;
|
||||
for (c2 = c; c2 && c2->tt == MRB_TT_SCLASS; c2 = mrb_class_ptr(klass)) {
|
||||
if (!iv_get(mrb, class_iv_ptr(c2), MRB_SYM(__attached__), &klass)) {
|
||||
c2 = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c2 && (c2->tt == MRB_TT_CLASS || c2->tt == MRB_TT_MODULE)) c = c2;
|
||||
}
|
||||
return const_get(mrb, c, sym, TRUE);
|
||||
}
|
||||
@@ -926,6 +926,13 @@ const_i(mrb_state *mrb, mrb_sym sym, mrb_value v, void *p)
|
||||
return 0;
|
||||
}
|
||||
|
||||
mrb_value
|
||||
mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary)
|
||||
{
|
||||
iv_foreach(mrb, class_iv_ptr(c), const_i, &ary);
|
||||
return ary;
|
||||
}
|
||||
|
||||
/* 15.2.2.4.24 */
|
||||
/*
|
||||
* call-seq:
|
||||
@@ -943,7 +950,7 @@ mrb_mod_constants(mrb_state *mrb, mrb_value mod)
|
||||
mrb_get_args(mrb, "|b", &inherit);
|
||||
ary = mrb_ary_new(mrb);
|
||||
while (c) {
|
||||
iv_foreach(mrb, class_iv_ptr(c), const_i, &ary);
|
||||
mrb_mod_const_at(mrb, c, ary);
|
||||
if (!inherit) break;
|
||||
c = c->super;
|
||||
if (c == mrb->object_class) break;
|
||||
|
||||
@@ -845,7 +845,7 @@ mrb_value
|
||||
mrb_object_exec(mrb_state *mrb, mrb_value self, struct RClass *target_class)
|
||||
{
|
||||
mrb_callinfo *ci = mrb->c->ci;
|
||||
int bidx = mrb_ci_bidx(ci);
|
||||
mrb_int bidx = ci_bidx(ci);
|
||||
mrb_value blk = ci->stack[bidx];
|
||||
if (mrb_nil_p(blk)) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "no block given");
|
||||
@@ -1915,7 +1915,7 @@ RETRY_TRY_BLOCK:
|
||||
}
|
||||
else if ((m.flags & MRB_METHOD_PROTECTED_FL) && mrb_obj_is_kind_of(mrb, recv, ci->u.target_class)) {
|
||||
mrb_value args = (ci->n == 15) ? regs[1] : mrb_ary_new_from_values(mrb, ci->n, regs+1);
|
||||
mrb_no_method_error(mrb, mid, args, "proteced method '%n' called for %T", mid, recv);
|
||||
mrb_no_method_error(mrb, mid, args, "protected method '%n' called for %T", mid, recv);
|
||||
}
|
||||
}
|
||||
ci->cci = CINFO_NONE;
|
||||
|
||||
@@ -35,7 +35,7 @@ MRuby::Toolchain.new(:visualcpp) do |conf, _params|
|
||||
|
||||
conf.gperf do |gperf|
|
||||
gperf.command = 'gperf.exe'
|
||||
gperf.compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
|
||||
gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
|
||||
end
|
||||
|
||||
conf.exts do |exts|
|
||||
|
||||
+29
-2
@@ -802,14 +802,18 @@ assert('method visibility') do
|
||||
|
||||
v = CallTypeTest.new
|
||||
|
||||
assert_raise(NoMethodError) { v.func { :test } }
|
||||
assert_raise_with_message_pattern(NameError, "private method 'func' called for CallTypeTest") do
|
||||
v.func { :test }
|
||||
end
|
||||
assert_equal :test, v.test_private { :test }
|
||||
|
||||
class CallTypeTest
|
||||
protected :func
|
||||
end
|
||||
|
||||
assert_raise(NoMethodError) { v.func { :test } }
|
||||
assert_raise_with_message_pattern(NameError, "protected method 'func' called for CallTypeTest") do
|
||||
v.func { :test }
|
||||
end
|
||||
assert_equal :test, v.test_protected { :test }
|
||||
assert_equal :test, v.test_private { :test }
|
||||
|
||||
@@ -834,6 +838,9 @@ assert('Module#module_function') do
|
||||
end
|
||||
|
||||
assert_true M.respond_to?(:modfunc)
|
||||
assert_equal nil do
|
||||
M.modfunc
|
||||
end
|
||||
end
|
||||
|
||||
assert('module with non-class/module outer raises TypeError') do
|
||||
@@ -882,3 +889,23 @@ assert('shared empty iv_tbl (prepend)') do
|
||||
m2::CONST2 = 2
|
||||
assert_equal 2, c::CONST2
|
||||
end
|
||||
|
||||
assert('constant lookup #6506') do
|
||||
Module.new do
|
||||
module X
|
||||
module A
|
||||
class WWW; end
|
||||
end
|
||||
end
|
||||
|
||||
module X::Y; end
|
||||
|
||||
module X::Y::Z
|
||||
extend X::A
|
||||
|
||||
class << self
|
||||
assert_nothing_raised{WWW}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+346
-31
@@ -1,12 +1,327 @@
|
||||
# NEWS for Lrama
|
||||
|
||||
## Lrama 0.7.0 (2025-01-21)
|
||||
|
||||
## [EXPERIMENTAL] Support the generation of the IELR(1) parser described in this paper
|
||||
|
||||
Support the generation of the IELR(1) parser described in this paper.
|
||||
https://www.sciencedirect.com/science/article/pii/S0167642309001191
|
||||
|
||||
If you use IELR(1) parser, you can write the following directive in your grammar file.
|
||||
|
||||
```yacc
|
||||
%define lr.type ielr
|
||||
```
|
||||
|
||||
But, currently IELR(1) parser is experimental feature. If you find any bugs, please report it to us. Thank you.
|
||||
|
||||
## Support `-t` option as same as `--debug` option
|
||||
|
||||
Support to `-t` option as same as `--debug` option.
|
||||
These options align with Bison behavior. So same as `--debug` option.
|
||||
|
||||
## Trace only explicit rules
|
||||
|
||||
Support to trace only explicit rules.
|
||||
If you use `--trace=rules` option, it shows include mid-rule actions. If you want to show only explicit rules, you can use `--trace=only-explicit-rules` option.
|
||||
|
||||
Example:
|
||||
|
||||
```yacc
|
||||
%{
|
||||
%}
|
||||
%union {
|
||||
int i;
|
||||
}
|
||||
%token <i> number
|
||||
%type <i> program
|
||||
%%
|
||||
program : number { printf("%d", $1); } number { $$ = $1 + $3; }
|
||||
;
|
||||
%%
|
||||
```
|
||||
|
||||
Result of `--trace=rules`:
|
||||
|
||||
```console
|
||||
$ exe/lrama --trace=rules sample.y
|
||||
Grammar rules:
|
||||
$accept -> program YYEOF
|
||||
$@1 -> ε
|
||||
program -> number $@1 number
|
||||
```
|
||||
|
||||
Result of `--trace=only-explicit-rules`:
|
||||
|
||||
```console
|
||||
$ exe/lrama --trace=explicit-rules sample.y
|
||||
Grammar rules:
|
||||
$accept -> program YYEOF
|
||||
program -> number number
|
||||
```
|
||||
|
||||
## Lrama 0.6.11 (2024-12-23)
|
||||
|
||||
### Add support for %type declarations using %nterm in Nonterminal Symbols
|
||||
|
||||
Allow to use `%nterm` in Nonterminal Symbols for `%type` declarations.
|
||||
|
||||
```yacc
|
||||
%nterm <type> nonterminal…
|
||||
```
|
||||
|
||||
This directive is also supported for compatibility with Bison, and only non-terminal symbols are allowed. In other words, definitions like the following will result in an error:
|
||||
|
||||
```yacc
|
||||
%{
|
||||
// Prologue
|
||||
%}
|
||||
|
||||
%token EOI 0 "EOI"
|
||||
%nterm EOI
|
||||
|
||||
%%
|
||||
|
||||
program: /* empty */
|
||||
;
|
||||
```
|
||||
|
||||
It show an error message like the following:
|
||||
|
||||
```command
|
||||
❯ exe/lrama nterm.y
|
||||
nterm.y:6:7: symbol EOI redeclared as a nonterminal
|
||||
%nterm EOI
|
||||
^^^
|
||||
```
|
||||
|
||||
## Lrama 0.6.10 (2024-09-11)
|
||||
|
||||
### Aliased Named References for actions of RHS in parameterizing rules
|
||||
|
||||
Allow to use aliased named references for actions of RHS in parameterizing rules.
|
||||
|
||||
```yacc
|
||||
%rule sum(X, Y): X[summand] '+' Y[addend] { $$ = $summand + $addend }
|
||||
;
|
||||
```
|
||||
|
||||
https://github.com/ruby/lrama/pull/410
|
||||
|
||||
### Named References for actions of RHS in parameterizing rules caller side
|
||||
|
||||
Allow to use named references for actions of RHS in parameterizing rules caller side.
|
||||
|
||||
```yacc
|
||||
opt_nl: '\n'?[nl] <str> { $$ = $nl; }
|
||||
;
|
||||
```
|
||||
|
||||
https://github.com/ruby/lrama/pull/414
|
||||
|
||||
### Widen the definable position of parameterizing rules
|
||||
|
||||
Allow to define parameterizing rules in the middle of the grammar.
|
||||
|
||||
```yacc
|
||||
%rule defined_option(X): /* empty */
|
||||
| X
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
program : defined_option(number) <i>
|
||||
| defined_list(number) <i>
|
||||
;
|
||||
|
||||
%rule defined_list(X): /* empty */ /* <--- here */
|
||||
| defined_list(X) number
|
||||
;
|
||||
```
|
||||
|
||||
https://github.com/ruby/lrama/pull/420
|
||||
|
||||
### Report unused terminal symbols
|
||||
|
||||
Support to report unused terminal symbols.
|
||||
Run `exe/lrama --report=terms` to show unused terminal symbols.
|
||||
|
||||
```console
|
||||
$ exe/lrama --report=terms sample/calc.y
|
||||
11 Unused Terms
|
||||
0 YYerror
|
||||
1 YYUNDEF
|
||||
2 '\\\\'
|
||||
3 '\\13'
|
||||
4 keyword_class2
|
||||
5 tNUMBER
|
||||
6 tPLUS
|
||||
7 tMINUS
|
||||
8 tEQ
|
||||
9 tEQEQ
|
||||
10 '>'
|
||||
```
|
||||
|
||||
https://github.com/ruby/lrama/pull/439
|
||||
|
||||
### Report unused rules
|
||||
|
||||
Support to report unused rules.
|
||||
Run `exe/lrama --report=rules` to show unused rules.
|
||||
|
||||
```console
|
||||
$ exe/lrama --report=rules sample/calc.y
|
||||
3 Unused Rules
|
||||
0 unused_option
|
||||
1 unused_list
|
||||
2 unused_nonempty_list
|
||||
```
|
||||
|
||||
https://github.com/ruby/lrama/pull/441
|
||||
|
||||
### Ensure compatibility with Bison for `%locations` directive
|
||||
|
||||
Support `%locations` directive to ensure compatibility with Bison.
|
||||
Change to `%locations` directive not set by default.
|
||||
|
||||
https://github.com/ruby/lrama/pull/446
|
||||
|
||||
### Diagnostics report for parameterizing rules redefine
|
||||
|
||||
Support to warning redefined parameterizing rules.
|
||||
Run `exe/lrama -W` or `exe/lrama --warnings` to show redefined parameterizing rules.
|
||||
|
||||
```console
|
||||
$ exe/lrama -W sample/calc.y
|
||||
parameterizing rule redefined: redefined_method(X)
|
||||
parameterizing rule redefined: redefined_method(X)
|
||||
```
|
||||
|
||||
https://github.com/ruby/lrama/pull/448
|
||||
|
||||
### Support `-v` and `--verbose` option
|
||||
|
||||
Support to `-v` and `--verbose` option.
|
||||
These options align with Bison behavior. So same as '--report=state' option.
|
||||
|
||||
https://github.com/ruby/lrama/pull/457
|
||||
|
||||
## Lrama 0.6.9 (2024-05-02)
|
||||
|
||||
### Callee side tag specification of parameterizing rules
|
||||
|
||||
Allow to specify tag on callee side of parameterizing rules.
|
||||
|
||||
```yacc
|
||||
%union {
|
||||
int i;
|
||||
}
|
||||
|
||||
%rule with_tag(X) <i>: X { $$ = $1; }
|
||||
;
|
||||
```
|
||||
|
||||
### Named References for actions of RHS in parameterizing rules
|
||||
|
||||
Allow to use named references for actions of RHS in parameterizing rules.
|
||||
|
||||
```yacc
|
||||
%rule option(number): /* empty */
|
||||
| number { $$ = $number; }
|
||||
;
|
||||
```
|
||||
|
||||
## Lrama 0.6.8 (2024-04-29)
|
||||
|
||||
### Nested parameterizing rules with tag
|
||||
|
||||
Allow to nested parameterizing rules with tag.
|
||||
|
||||
```yacc
|
||||
%union {
|
||||
int i;
|
||||
}
|
||||
|
||||
%rule nested_nested_option(X): /* empty */
|
||||
| X
|
||||
;
|
||||
|
||||
%rule nested_option(X): /* empty */
|
||||
| nested_nested_option(X) <i>
|
||||
;
|
||||
|
||||
%rule option(Y): /* empty */
|
||||
| nested_option(Y) <i>
|
||||
;
|
||||
```
|
||||
|
||||
## Lrama 0.6.7 (2024-04-28)
|
||||
|
||||
### RHS of user defined parameterizing rules contains `'symbol'?`, `'symbol'+` and `'symbol'*`.
|
||||
|
||||
User can use `'symbol'?`, `'symbol'+` and `'symbol'*` in RHS of user defined parameterizing rules.
|
||||
|
||||
```
|
||||
%rule with_word_seps(X): /* empty */
|
||||
| X ' '+
|
||||
;
|
||||
```
|
||||
|
||||
## Lrama 0.6.6 (2024-04-27)
|
||||
|
||||
### Trace actions
|
||||
|
||||
Support trace actions for debugging.
|
||||
Run `exe/lrama --trace=actions` to show grammar rules with actions.
|
||||
|
||||
```console
|
||||
$ exe/lrama --trace=actions sample/calc.y
|
||||
Grammar rules with actions:
|
||||
$accept -> list, YYEOF {}
|
||||
list -> ε {}
|
||||
list -> list, LF {}
|
||||
list -> list, expr, LF { printf("=> %d\n", $2); }
|
||||
expr -> NUM {}
|
||||
expr -> expr, '+', expr { $$ = $1 + $3; }
|
||||
expr -> expr, '-', expr { $$ = $1 - $3; }
|
||||
expr -> expr, '*', expr { $$ = $1 * $3; }
|
||||
expr -> expr, '/', expr { $$ = $1 / $3; }
|
||||
expr -> '(', expr, ')' { $$ = $2; }
|
||||
```
|
||||
|
||||
### Inlining
|
||||
|
||||
Support inlining for rules.
|
||||
The `%inline` directive causes all references to symbols to be replaced with its definition.
|
||||
|
||||
```yacc
|
||||
%rule %inline op: PLUS { + }
|
||||
| TIMES { * }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
expr : number { $$ = $1; }
|
||||
| expr op expr { $$ = $1 $2 $3; }
|
||||
;
|
||||
```
|
||||
|
||||
as same as
|
||||
|
||||
```yacc
|
||||
expr : number { $$ = $1; }
|
||||
| expr '+' expr { $$ = $1 + $3; }
|
||||
| expr '*' expr { $$ = $1 * $3; }
|
||||
;
|
||||
```
|
||||
|
||||
## Lrama 0.6.5 (2024-03-25)
|
||||
|
||||
### Typed Midrule Actions
|
||||
|
||||
User can specify the type of mid rule action by tag (`<bar>`) instead of specifying it with in an action.
|
||||
|
||||
```
|
||||
```yacc
|
||||
primary: k_case expr_value terms?
|
||||
{
|
||||
$<val>$ = p->case_labels;
|
||||
@@ -21,7 +336,7 @@ primary: k_case expr_value terms?
|
||||
|
||||
can be written as
|
||||
|
||||
```
|
||||
```yacc
|
||||
primary: k_case expr_value terms?
|
||||
{
|
||||
$$ = p->case_labels;
|
||||
@@ -46,7 +361,7 @@ Bison supports this feature from 3.1.
|
||||
|
||||
Support `preceded`, `terminated` and `delimited` rules.
|
||||
|
||||
```
|
||||
```text
|
||||
program: preceded(opening, X)
|
||||
|
||||
// Expanded to
|
||||
@@ -73,7 +388,7 @@ program: delimited_opening_X_closing
|
||||
delimited_opening_X_closing: opening X closing
|
||||
```
|
||||
|
||||
<https://github.com/ruby/lrama/pull/382>
|
||||
https://github.com/ruby/lrama/pull/382
|
||||
|
||||
### Support `%destructor` declaration
|
||||
|
||||
@@ -82,7 +397,7 @@ In general, these resources are freed by actions or after parsing.
|
||||
However if syntax error happens in parsing, these codes may not be executed.
|
||||
Codes associated to `%destructor` are executed when semantic value is popped from the stack by an error.
|
||||
|
||||
```
|
||||
```yacc
|
||||
%token <val1> NUM
|
||||
%type <val2> expr2
|
||||
%type <val3> expr
|
||||
@@ -102,7 +417,7 @@ Codes associated to `%destructor` are executed when semantic value is popped fro
|
||||
|
||||
Bison supports this feature from 1.75b.
|
||||
|
||||
<https://github.com/ruby/lrama/pull/385>
|
||||
https://github.com/ruby/lrama/pull/385
|
||||
|
||||
## Lrama 0.6.3 (2024-02-15)
|
||||
|
||||
@@ -130,7 +445,7 @@ Lrama provides these five callbacks. Registered functions are called when each e
|
||||
User also needs to access semantic value of their stack in grammar action. `$:n` provides the way to access to it. `$:n` is translated to the minus index from the top of the stack.
|
||||
For example
|
||||
|
||||
```
|
||||
```yacc
|
||||
primary: k_if expr_value then compstmt if_tail k_end
|
||||
{
|
||||
/*% ripper: if!($:2, $:4, $:5) %*/
|
||||
@@ -138,7 +453,7 @@ primary: k_if expr_value then compstmt if_tail k_end
|
||||
}
|
||||
```
|
||||
|
||||
<https://github.com/ruby/lrama/pull/367>
|
||||
https://github.com/ruby/lrama/pull/367
|
||||
|
||||
## Lrama 0.6.2 (2024-01-27)
|
||||
|
||||
@@ -147,7 +462,7 @@ primary: k_if expr_value then compstmt if_tail k_end
|
||||
If `%no-stdlib` directive is set, Lrama doesn't load Lrama standard library for
|
||||
parameterizing rules, stdlib.y.
|
||||
|
||||
<https://github.com/ruby/lrama/pull/344>
|
||||
https://github.com/ruby/lrama/pull/344
|
||||
|
||||
## Lrama 0.6.1 (2024-01-13)
|
||||
|
||||
@@ -155,7 +470,7 @@ parameterizing rules, stdlib.y.
|
||||
|
||||
Allow to pass an instantiated rule to other parameterizing rules.
|
||||
|
||||
```
|
||||
```yacc
|
||||
%rule constant(X) : X
|
||||
;
|
||||
|
||||
@@ -172,7 +487,7 @@ program : option(constant(number)) // Nested rule
|
||||
|
||||
Allow to use nested parameterizing rules when define parameterizing rules.
|
||||
|
||||
```
|
||||
```yacc
|
||||
%rule option(x) : /* empty */
|
||||
| X
|
||||
;
|
||||
@@ -191,7 +506,7 @@ program : double_opt(number)
|
||||
%%
|
||||
```
|
||||
|
||||
<https://github.com/ruby/lrama/pull/337>
|
||||
https://github.com/ruby/lrama/pull/337
|
||||
|
||||
## Lrama 0.6.0 (2023-12-25)
|
||||
|
||||
@@ -199,7 +514,7 @@ program : double_opt(number)
|
||||
|
||||
Allow to define parameterizing rule by `%rule` directive.
|
||||
|
||||
```
|
||||
```yacc
|
||||
%rule pair(X, Y): X Y { $$ = $1 + $2; }
|
||||
;
|
||||
|
||||
@@ -213,7 +528,7 @@ stmt: pair(ODD, EVEN) <num>
|
||||
;
|
||||
```
|
||||
|
||||
<https://github.com/ruby/lrama/pull/285>
|
||||
https://github.com/ruby/lrama/pull/285
|
||||
|
||||
## Lrama 0.5.11 (2023-12-02)
|
||||
|
||||
@@ -222,7 +537,7 @@ stmt: pair(ODD, EVEN) <num>
|
||||
Allow to specify type of rules by specifying tag, `<i>` in below example.
|
||||
Tag is post-modification style.
|
||||
|
||||
```
|
||||
```yacc
|
||||
%union {
|
||||
int i;
|
||||
}
|
||||
@@ -234,7 +549,7 @@ program : option(number) <i>
|
||||
;
|
||||
```
|
||||
|
||||
<https://github.com/ruby/lrama/pull/272>
|
||||
https://github.com/ruby/lrama/pull/272
|
||||
|
||||
## Lrama 0.5.10 (2023-11-18)
|
||||
|
||||
@@ -242,13 +557,13 @@ program : option(number) <i>
|
||||
|
||||
Support function call style parameterizing rules for `option`, `nonempty_list` and `list`.
|
||||
|
||||
<https://github.com/ruby/lrama/pull/197>
|
||||
https://github.com/ruby/lrama/pull/197
|
||||
|
||||
### Parameterizing rules (separated_list)
|
||||
|
||||
Support `separated_list` and `separated_nonempty_list` parameterizing rules.
|
||||
|
||||
```
|
||||
```text
|
||||
program: separated_list(',', number)
|
||||
|
||||
// Expanded to
|
||||
@@ -270,7 +585,7 @@ separated_nonempty_list_number: number
|
||||
separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
||||
```
|
||||
|
||||
<https://github.com/ruby/lrama/pull/204>
|
||||
https://github.com/ruby/lrama/pull/204
|
||||
|
||||
## Lrama 0.5.9 (2023-11-05)
|
||||
|
||||
@@ -279,7 +594,7 @@ separated_nonempty_list_number: separated_nonempty_list_number ',' number
|
||||
Parameterizing rules are template of rules.
|
||||
It's very common pattern to write "list" grammar rule like:
|
||||
|
||||
```
|
||||
```yacc
|
||||
opt_args: /* none */
|
||||
| args
|
||||
;
|
||||
@@ -294,18 +609,18 @@ Lrama supports these suffixes:
|
||||
- `+`: nonempty list
|
||||
- `*`: list
|
||||
|
||||
Idea of Parameterizing rules comes from Menhir LR(1) parser generator (<https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32>).
|
||||
Idea of Parameterizing rules comes from Menhir LR(1) parser generator (https://gallium.inria.fr/~fpottier/menhir/manual.html#sec32).
|
||||
|
||||
<https://github.com/ruby/lrama/pull/181>
|
||||
https://github.com/ruby/lrama/pull/181
|
||||
|
||||
## Lrama 0.5.7 (2023-10-23)
|
||||
|
||||
### Racc parser
|
||||
|
||||
Replace Lrama's parser from hand written parser to LR parser generated by Racc.
|
||||
Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (<https://github.com/ruby/lrama/pull/132>).
|
||||
Lrama uses `--embedded` option to generate LR parser because Racc is changed from default gem to bundled gem by Ruby 3.3 (https://github.com/ruby/lrama/pull/132).
|
||||
|
||||
<https://github.com/ruby/lrama/pull/62>
|
||||
https://github.com/ruby/lrama/pull/62
|
||||
|
||||
## Lrama 0.5.4 (2023-08-17)
|
||||
|
||||
@@ -316,7 +631,7 @@ Make error recovery function configurable on runtime by two new macros.
|
||||
- `YYMAXREPAIR`: Expected to return max length of repair operations. `%parse-param` is passed to this function.
|
||||
- `YYERROR_RECOVERY_ENABLED`: Expected to return bool value to determine error recovery is enabled or not. `%parse-param` is passed to this function.
|
||||
|
||||
<https://github.com/ruby/lrama/pull/74>
|
||||
https://github.com/ruby/lrama/pull/74
|
||||
|
||||
## Lrama 0.5.3 (2023-08-05)
|
||||
|
||||
@@ -325,7 +640,7 @@ Make error recovery function configurable on runtime by two new macros.
|
||||
Support token insert base Error Recovery.
|
||||
`-e` option is needed to generate parser with error recovery functions.
|
||||
|
||||
<https://github.com/ruby/lrama/pull/44>
|
||||
https://github.com/ruby/lrama/pull/44
|
||||
|
||||
## Lrama 0.5.2 (2023-06-14)
|
||||
|
||||
@@ -334,7 +649,7 @@ Support token insert base Error Recovery.
|
||||
Instead of positional references like `$1` or `$$`,
|
||||
named references allow to access to symbol by name.
|
||||
|
||||
```
|
||||
```yacc
|
||||
primary: k_class cpath superclass bodystmt k_end
|
||||
{
|
||||
$primary = new_class($cpath, $bodystmt, $superclass);
|
||||
@@ -343,7 +658,7 @@ primary: k_class cpath superclass bodystmt k_end
|
||||
|
||||
Alias name can be declared.
|
||||
|
||||
```
|
||||
```yacc
|
||||
expr[result]: expr[ex-left] '+' expr[ex.right]
|
||||
{
|
||||
$result = $[ex-left] + $[ex.right];
|
||||
@@ -363,9 +678,9 @@ Bison supports this feature from 2.5.
|
||||
- `YY_REDUCE_PRINT`
|
||||
- `yysyntax_error`
|
||||
|
||||
<https://github.com/ruby/lrama/pull/40>
|
||||
https://github.com/ruby/lrama/pull/40
|
||||
|
||||
See also: <https://github.com/ruby/ruby/pull/7807>
|
||||
See also: https://github.com/ruby/ruby/pull/7807
|
||||
|
||||
## Lrama 0.5.0 (2023-05-17)
|
||||
|
||||
@@ -373,7 +688,7 @@ See also: <https://github.com/ruby/ruby/pull/7807>
|
||||
|
||||
When `-` is given as grammar file name, reads the grammar source from STDIN, and takes the next argument as the input file name. This mode helps pre-process a grammar source.
|
||||
|
||||
<https://github.com/ruby/lrama/pull/8>
|
||||
https://github.com/ruby/lrama/pull/8
|
||||
|
||||
## Lrama 0.4.0 (2023-05-13)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
$LOAD_PATH << File.join(__dir__, "../lib")
|
||||
require "lrama"
|
||||
|
||||
+22
-17
@@ -1,17 +1,22 @@
|
||||
require "lrama/bitmap"
|
||||
require "lrama/command"
|
||||
require "lrama/context"
|
||||
require "lrama/counterexamples"
|
||||
require "lrama/digraph"
|
||||
require "lrama/grammar"
|
||||
require "lrama/lexer"
|
||||
require "lrama/option_parser"
|
||||
require "lrama/options"
|
||||
require "lrama/output"
|
||||
require "lrama/parser"
|
||||
require "lrama/report"
|
||||
require "lrama/state"
|
||||
require "lrama/states"
|
||||
require "lrama/states_reporter"
|
||||
require "lrama/version"
|
||||
require "lrama/warning"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "lrama/bitmap"
|
||||
require_relative "lrama/command"
|
||||
require_relative "lrama/context"
|
||||
require_relative "lrama/counterexamples"
|
||||
require_relative "lrama/diagnostics"
|
||||
require_relative "lrama/digraph"
|
||||
require_relative "lrama/grammar"
|
||||
require_relative "lrama/grammar_validator"
|
||||
require_relative "lrama/lexer"
|
||||
require_relative "lrama/logger"
|
||||
require_relative "lrama/option_parser"
|
||||
require_relative "lrama/options"
|
||||
require_relative "lrama/output"
|
||||
require_relative "lrama/parser"
|
||||
require_relative "lrama/report"
|
||||
require_relative "lrama/state"
|
||||
require_relative "lrama/states"
|
||||
require_relative "lrama/states_reporter"
|
||||
require_relative "lrama/trace_reporter"
|
||||
require_relative "lrama/version"
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
module Bitmap
|
||||
# @rbs (Array[Integer] ary) -> Integer
|
||||
def self.from_array(ary)
|
||||
bit = 0
|
||||
|
||||
@@ -10,8 +14,9 @@ module Lrama
|
||||
bit
|
||||
end
|
||||
|
||||
# @rbs (Integer int) -> Array[Integer]
|
||||
def self.to_array(int)
|
||||
a = []
|
||||
a = [] #: Array[Integer]
|
||||
i = 0
|
||||
|
||||
while int > 0 do
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Command
|
||||
LRAMA_LIB = File.realpath(File.join(File.dirname(__FILE__)))
|
||||
@@ -14,11 +16,10 @@ module Lrama
|
||||
|
||||
Report::Duration.enable if options.trace_opts[:time]
|
||||
|
||||
warning = Lrama::Warning.new
|
||||
text = options.y.read
|
||||
options.y.close if options.y != STDIN
|
||||
begin
|
||||
grammar = Lrama::Parser.new(text, options.grammar_file, options.debug).parse
|
||||
grammar = Lrama::Parser.new(text, options.grammar_file, options.debug, options.define).parse
|
||||
unless grammar.no_stdlib
|
||||
stdlib_grammar = Lrama::Parser.new(File.read(STDLIB_FILE_PATH), STDLIB_FILE_PATH, options.debug).parse
|
||||
grammar.insert_before_parameterizing_rules(stdlib_grammar.parameterizing_rules)
|
||||
@@ -31,8 +32,9 @@ module Lrama
|
||||
message = message.gsub(/.+/, "\e[1m\\&\e[m") if Exception.to_tty?
|
||||
abort message
|
||||
end
|
||||
states = Lrama::States.new(grammar, warning, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
|
||||
states = Lrama::States.new(grammar, trace_state: (options.trace_opts[:automaton] || options.trace_opts[:closure]))
|
||||
states.compute
|
||||
states.compute_ielr if grammar.ielr_defined?
|
||||
context = Lrama::Context.new(states)
|
||||
|
||||
if options.report_file
|
||||
@@ -42,15 +44,8 @@ module Lrama
|
||||
end
|
||||
end
|
||||
|
||||
if options.trace_opts && options.trace_opts[:rules]
|
||||
puts "Grammar rules:"
|
||||
puts grammar.rules
|
||||
end
|
||||
|
||||
if options.trace_opts && options.trace_opts[:actions]
|
||||
puts "Grammar rules with actions:"
|
||||
grammar.rules.each { |rule| puts rule.with_actions }
|
||||
end
|
||||
reporter = Lrama::TraceReporter.new(grammar)
|
||||
reporter.report(**options.trace_opts)
|
||||
|
||||
File.open(options.outfile, "w+") do |f|
|
||||
Lrama::Output.new(
|
||||
@@ -65,9 +60,9 @@ module Lrama
|
||||
).render
|
||||
end
|
||||
|
||||
if warning.has_error?
|
||||
exit false
|
||||
end
|
||||
logger = Lrama::Logger.new
|
||||
exit false unless Lrama::GrammarValidator.new(grammar, states, logger).valid?
|
||||
Lrama::Diagnostics.new(grammar, states, logger).run(options.diagnostic)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
require "lrama/report/duration"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "report/duration"
|
||||
|
||||
module Lrama
|
||||
# This is passed to a template
|
||||
@@ -253,7 +255,7 @@ module Lrama
|
||||
|
||||
# If no default_reduction_rule, default behavior is an
|
||||
# error then replace ErrorActionNumber with zero.
|
||||
if !state.default_reduction_rule
|
||||
unless state.default_reduction_rule
|
||||
actions.map! do |e|
|
||||
if e == ErrorActionNumber
|
||||
0
|
||||
@@ -301,10 +303,7 @@ module Lrama
|
||||
end
|
||||
|
||||
@states.nterms.each do |nterm|
|
||||
if !(states = nterm_to_next_states[nterm])
|
||||
default_goto = 0
|
||||
not_default_gotos = []
|
||||
else
|
||||
if (states = nterm_to_next_states[nterm])
|
||||
default_state = states.map(&:last).group_by {|s| s }.max_by {|_, v| v.count }.first
|
||||
default_goto = default_state.id
|
||||
not_default_gotos = []
|
||||
@@ -312,6 +311,9 @@ module Lrama
|
||||
next if to_state.id == default_goto
|
||||
not_default_gotos << [from_state.id, to_state.id]
|
||||
end
|
||||
else
|
||||
default_goto = 0
|
||||
not_default_gotos = []
|
||||
end
|
||||
|
||||
k = nterm_number_to_sequence_number(nterm.number)
|
||||
@@ -403,7 +405,7 @@ module Lrama
|
||||
@check = []
|
||||
# Key is froms_and_tos, value is index position
|
||||
pushed = {}
|
||||
userd_res = {}
|
||||
used_res = {}
|
||||
lowzero = 0
|
||||
high = 0
|
||||
|
||||
@@ -428,7 +430,7 @@ module Lrama
|
||||
end
|
||||
end
|
||||
|
||||
if ok && userd_res[res]
|
||||
if ok && used_res[res]
|
||||
ok = false
|
||||
end
|
||||
|
||||
@@ -456,7 +458,7 @@ module Lrama
|
||||
|
||||
@base[state_id] = res
|
||||
pushed[froms_and_tos] = res
|
||||
userd_res[res] = true
|
||||
used_res[res] = true
|
||||
end
|
||||
|
||||
@yylast = high
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "set"
|
||||
|
||||
require "lrama/counterexamples/derivation"
|
||||
require "lrama/counterexamples/example"
|
||||
require "lrama/counterexamples/path"
|
||||
require "lrama/counterexamples/production_path"
|
||||
require "lrama/counterexamples/start_path"
|
||||
require "lrama/counterexamples/state_item"
|
||||
require "lrama/counterexamples/transition_path"
|
||||
require "lrama/counterexamples/triple"
|
||||
require_relative "counterexamples/derivation"
|
||||
require_relative "counterexamples/example"
|
||||
require_relative "counterexamples/path"
|
||||
require_relative "counterexamples/production_path"
|
||||
require_relative "counterexamples/start_path"
|
||||
require_relative "counterexamples/state_item"
|
||||
require_relative "counterexamples/transition_path"
|
||||
require_relative "counterexamples/triple"
|
||||
|
||||
module Lrama
|
||||
# See: https://www.cs.cornell.edu/andru/papers/cupex/cupex.pdf
|
||||
@@ -30,8 +32,10 @@ module Lrama
|
||||
conflict_state.conflicts.flat_map do |conflict|
|
||||
case conflict.type
|
||||
when :shift_reduce
|
||||
# @type var conflict: State::ShiftReduceConflict
|
||||
shift_reduce_example(conflict_state, conflict)
|
||||
when :reduce_reduce
|
||||
# @type var conflict: State::ReduceReduceConflict
|
||||
reduce_reduce_examples(conflict_state, conflict)
|
||||
end
|
||||
end.compact
|
||||
@@ -46,7 +50,7 @@ module Lrama
|
||||
@reverse_transitions = {}
|
||||
|
||||
@states.states.each do |src_state|
|
||||
trans = {}
|
||||
trans = {} #: Hash[Grammar::Symbol, State]
|
||||
|
||||
src_state.transitions.each do |shift, next_state|
|
||||
trans[shift.next_sym] = next_state
|
||||
@@ -64,6 +68,7 @@ module Lrama
|
||||
|
||||
@transitions[[src_state_item, sym]] = dest_state_item
|
||||
|
||||
# @type var key: [StateItem, Grammar::Symbol]
|
||||
key = [dest_state_item, sym]
|
||||
@reverse_transitions[key] ||= Set.new
|
||||
@reverse_transitions[key] << src_state_item
|
||||
@@ -80,7 +85,7 @@ module Lrama
|
||||
|
||||
@states.states.each do |state|
|
||||
# LHS => Set(Item)
|
||||
h = {}
|
||||
h = {} #: Hash[Grammar::Symbol, Set[States::Item]]
|
||||
|
||||
state.closure.each do |item|
|
||||
sym = item.lhs
|
||||
@@ -95,6 +100,7 @@ module Lrama
|
||||
|
||||
sym = item.next_sym
|
||||
state_item = StateItem.new(state, item)
|
||||
# @type var key: [State, Grammar::Symbol]
|
||||
key = [state, sym]
|
||||
|
||||
@productions[state_item] = h[sym]
|
||||
@@ -107,6 +113,7 @@ module Lrama
|
||||
|
||||
def shift_reduce_example(conflict_state, conflict)
|
||||
conflict_symbol = conflict.symbols.first
|
||||
# @type var shift_conflict_item: ::Lrama::States::Item
|
||||
shift_conflict_item = conflict_state.items.find { |item| item.next_sym == conflict_symbol }
|
||||
path2 = shortest_path(conflict_state, conflict.reduce.item, conflict_symbol)
|
||||
path1 = find_shift_conflict_shortest_path(path2, conflict_state, shift_conflict_item)
|
||||
@@ -151,12 +158,14 @@ module Lrama
|
||||
prev_state_item = prev_path&.to
|
||||
|
||||
if target_state_item == state_item || target_state_item.item.start_item?
|
||||
result.concat(reversed_reduce_path[_j..-1].map(&:to))
|
||||
result.concat(
|
||||
reversed_reduce_path[_j..-1] #: Array[StartPath|TransitionPath|ProductionPath]
|
||||
.map(&:to))
|
||||
break
|
||||
end
|
||||
|
||||
if target_state_item.item.beginning_of_rule?
|
||||
queue = []
|
||||
queue = [] #: Array[Array[StateItem]]
|
||||
queue << [target_state_item]
|
||||
|
||||
# Find reverse production
|
||||
@@ -171,10 +180,18 @@ module Lrama
|
||||
break
|
||||
end
|
||||
|
||||
if !si.item.beginning_of_rule?
|
||||
if si.item.beginning_of_rule?
|
||||
# @type var key: [State, Grammar::Symbol]
|
||||
key = [si.state, si.item.lhs]
|
||||
@reverse_productions[key].each do |item|
|
||||
state_item = StateItem.new(si.state, item)
|
||||
queue << (sis + [state_item])
|
||||
end
|
||||
else
|
||||
# @type var key: [StateItem, Grammar::Symbol]
|
||||
key = [si, si.item.previous_sym]
|
||||
@reverse_transitions[key].each do |prev_target_state_item|
|
||||
next if prev_target_state_item.state != prev_state_item.state
|
||||
next if prev_target_state_item.state != prev_state_item&.state
|
||||
sis.shift
|
||||
result.concat(sis)
|
||||
result << prev_target_state_item
|
||||
@@ -183,19 +200,14 @@ module Lrama
|
||||
queue.clear
|
||||
break
|
||||
end
|
||||
else
|
||||
key = [si.state, si.item.lhs]
|
||||
@reverse_productions[key].each do |item|
|
||||
state_item = StateItem.new(si.state, item)
|
||||
queue << (sis + [state_item])
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
# Find reverse transition
|
||||
# @type var key: [StateItem, Grammar::Symbol]
|
||||
key = [target_state_item, target_state_item.item.previous_sym]
|
||||
@reverse_transitions[key].each do |prev_target_state_item|
|
||||
next if prev_target_state_item.state != prev_state_item.state
|
||||
next if prev_target_state_item.state != prev_state_item&.state
|
||||
result << prev_target_state_item
|
||||
target_state_item = prev_target_state_item
|
||||
i = j
|
||||
@@ -222,9 +234,9 @@ module Lrama
|
||||
|
||||
def shortest_path(conflict_state, conflict_reduce_item, conflict_term)
|
||||
# queue: is an array of [Triple, [Path]]
|
||||
queue = []
|
||||
visited = {}
|
||||
start_state = @states.states.first
|
||||
queue = [] #: Array[[Triple, Array[StartPath|TransitionPath|ProductionPath]]]
|
||||
visited = {} #: Hash[Triple, true]
|
||||
start_state = @states.states.first #: Lrama::State
|
||||
raise "BUG: Start state should be just one kernel." if start_state.kernels.count != 1
|
||||
|
||||
start = Triple.new(start_state, start_state.kernels.first, Set.new([@states.eof_symbol]))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class Derivation
|
||||
@@ -16,7 +18,7 @@ module Lrama
|
||||
alias :inspect :to_s
|
||||
|
||||
def render_strings_for_report
|
||||
result = []
|
||||
result = [] #: Array[String]
|
||||
_render_for_report(self, 0, result, 0)
|
||||
result.map(&:rstrip)
|
||||
end
|
||||
@@ -42,18 +44,19 @@ module Lrama
|
||||
str << "#{item.next_sym.display_name}"
|
||||
length = _render_for_report(derivation.left, len, strings, index + 1)
|
||||
# I want String#ljust!
|
||||
str << " " * (length - str.length)
|
||||
str << " " * (length - str.length) if length > str.length
|
||||
else
|
||||
str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} "
|
||||
return str.length
|
||||
end
|
||||
|
||||
if derivation.right&.left
|
||||
length = _render_for_report(derivation.right.left, str.length, strings, index + 1)
|
||||
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
|
||||
left = derivation.right&.left #: Derivation
|
||||
length = _render_for_report(left, str.length, strings, index + 1)
|
||||
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore
|
||||
str << " " * (length - str.length) if length > str.length
|
||||
elsif item.next_next_sym
|
||||
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} "
|
||||
str << "#{item.symbols_after_dot[1..-1].map(&:display_name).join(" ")} " # steep:ignore
|
||||
end
|
||||
|
||||
return str.length
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class Example
|
||||
@@ -36,9 +38,10 @@ module Lrama
|
||||
private
|
||||
|
||||
def _derivations(paths)
|
||||
derivation = nil
|
||||
derivation = nil #: Derivation
|
||||
current = :production
|
||||
lookahead_sym = paths.last.to.item.end_of_rule? ? @conflict_symbol : nil
|
||||
last_path = paths.last #: Path
|
||||
lookahead_sym = last_path.to.item.end_of_rule? ? @conflict_symbol : nil
|
||||
|
||||
paths.reverse_each do |path|
|
||||
item = path.to.item
|
||||
@@ -55,12 +58,14 @@ module Lrama
|
||||
when ProductionPath
|
||||
derivation = Derivation.new(item, derivation)
|
||||
current = :production
|
||||
else
|
||||
raise "Unexpected. #{path}"
|
||||
end
|
||||
|
||||
if lookahead_sym && item.next_next_sym && item.next_next_sym.first_set.include?(lookahead_sym)
|
||||
state_item = @counterexamples.transitions[[path.to, item.next_sym]]
|
||||
derivation2 = find_derivation_for_symbol(state_item, lookahead_sym)
|
||||
derivation.right = derivation2
|
||||
derivation.right = derivation2 # steep:ignore
|
||||
lookahead_sym = nil
|
||||
end
|
||||
|
||||
@@ -87,7 +92,7 @@ module Lrama
|
||||
end
|
||||
|
||||
def find_derivation_for_symbol(state_item, sym)
|
||||
queue = []
|
||||
queue = [] #: Array[Array[StateItem]]
|
||||
queue << [state_item]
|
||||
|
||||
while (sis = queue.shift)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class Path
|
||||
@@ -18,6 +20,10 @@ module Lrama
|
||||
"#<Path(#{type})>"
|
||||
end
|
||||
alias :inspect :to_s
|
||||
|
||||
def type
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class ProductionPath < Path
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class StartPath < Path
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class StateItem < Struct.new(:state, :item)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
class TransitionPath < Path
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Counterexamples
|
||||
# s: state
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Diagnostics
|
||||
def initialize(grammar, states, logger)
|
||||
@grammar = grammar
|
||||
@states = states
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def run(diagnostic)
|
||||
if diagnostic
|
||||
diagnose_conflict
|
||||
diagnose_parameterizing_redefined
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def diagnose_conflict
|
||||
if @states.sr_conflicts_count != 0
|
||||
@logger.warn("shift/reduce conflicts: #{@states.sr_conflicts_count} found")
|
||||
end
|
||||
|
||||
if @states.rr_conflicts_count != 0
|
||||
@logger.warn("reduce/reduce conflicts: #{@states.rr_conflicts_count} found")
|
||||
end
|
||||
end
|
||||
|
||||
def diagnose_parameterizing_redefined
|
||||
@grammar.parameterizing_rule_resolver.redefined_rules.each do |rule|
|
||||
@logger.warn("parameterizing rule redefined: #{rule}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,21 +1,52 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
# Algorithm Digraph of https://dl.acm.org/doi/pdf/10.1145/69622.357187 (P. 625)
|
||||
#
|
||||
# @rbs generic X < Object -- Type of a member of `sets`
|
||||
# @rbs generic Y < _Or -- Type of sets assigned to a member of `sets`
|
||||
class Digraph
|
||||
# TODO: rbs-inline 0.10.0 doesn't support instance variables.
|
||||
# Move these type declarations above instance variable definitions, once it's supported.
|
||||
#
|
||||
# @rbs!
|
||||
# interface _Or
|
||||
# def |: (self) -> self
|
||||
# end
|
||||
# @sets: Array[X]
|
||||
# @relation: Hash[X, Array[X]]
|
||||
# @base_function: Hash[X, Y]
|
||||
# @stack: Array[X]
|
||||
# @h: Hash[X, (Integer|Float)?]
|
||||
# @result: Hash[X, Y]
|
||||
|
||||
# @rbs sets: Array[X]
|
||||
# @rbs relation: Hash[X, Array[X]]
|
||||
# @rbs base_function: Hash[X, Y]
|
||||
# @rbs return: void
|
||||
def initialize(sets, relation, base_function)
|
||||
|
||||
# X in the paper
|
||||
@sets = sets
|
||||
|
||||
# R in the paper
|
||||
@relation = relation
|
||||
|
||||
# F' in the paper
|
||||
@base_function = base_function
|
||||
|
||||
# S in the paper
|
||||
@stack = []
|
||||
|
||||
# N in the paper
|
||||
@h = Hash.new(0)
|
||||
|
||||
# F in the paper
|
||||
@result = {}
|
||||
end
|
||||
|
||||
# @rbs () -> Hash[X, Y]
|
||||
def compute
|
||||
@sets.each do |x|
|
||||
next if @h[x] != 0
|
||||
@@ -27,6 +58,7 @@ module Lrama
|
||||
|
||||
private
|
||||
|
||||
# @rbs (X x) -> void
|
||||
def traverse(x)
|
||||
@stack.push(x)
|
||||
d = @stack.count
|
||||
|
||||
@@ -1,44 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "forwardable"
|
||||
require "lrama/grammar/auxiliary"
|
||||
require "lrama/grammar/binding"
|
||||
require "lrama/grammar/code"
|
||||
require "lrama/grammar/counter"
|
||||
require "lrama/grammar/destructor"
|
||||
require "lrama/grammar/error_token"
|
||||
require "lrama/grammar/parameterizing_rule"
|
||||
require "lrama/grammar/percent_code"
|
||||
require "lrama/grammar/precedence"
|
||||
require "lrama/grammar/printer"
|
||||
require "lrama/grammar/reference"
|
||||
require "lrama/grammar/rule"
|
||||
require "lrama/grammar/rule_builder"
|
||||
require "lrama/grammar/symbol"
|
||||
require "lrama/grammar/symbols"
|
||||
require "lrama/grammar/type"
|
||||
require "lrama/grammar/union"
|
||||
require "lrama/lexer"
|
||||
require_relative "grammar/auxiliary"
|
||||
require_relative "grammar/binding"
|
||||
require_relative "grammar/code"
|
||||
require_relative "grammar/counter"
|
||||
require_relative "grammar/destructor"
|
||||
require_relative "grammar/error_token"
|
||||
require_relative "grammar/parameterizing_rule"
|
||||
require_relative "grammar/percent_code"
|
||||
require_relative "grammar/precedence"
|
||||
require_relative "grammar/printer"
|
||||
require_relative "grammar/reference"
|
||||
require_relative "grammar/rule"
|
||||
require_relative "grammar/rule_builder"
|
||||
require_relative "grammar/symbol"
|
||||
require_relative "grammar/symbols"
|
||||
require_relative "grammar/type"
|
||||
require_relative "grammar/union"
|
||||
require_relative "lexer"
|
||||
|
||||
module Lrama
|
||||
# Grammar is the result of parsing an input grammar file
|
||||
class Grammar
|
||||
extend Forwardable
|
||||
|
||||
attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux
|
||||
attr_accessor :union, :expect,
|
||||
:printers, :error_tokens,
|
||||
:lex_param, :parse_param, :initial_action,
|
||||
attr_reader :percent_codes, :eof_symbol, :error_symbol, :undef_symbol, :accept_symbol, :aux, :parameterizing_rule_resolver
|
||||
attr_accessor :union, :expect, :printers, :error_tokens, :lex_param, :parse_param, :initial_action,
|
||||
:after_shift, :before_reduce, :after_reduce, :after_shift_error_token, :after_pop_stack,
|
||||
:symbols_resolver, :types,
|
||||
:rules, :rule_builders,
|
||||
:sym_to_rules, :no_stdlib
|
||||
:symbols_resolver, :types, :rules, :rule_builders, :sym_to_rules, :no_stdlib, :locations, :define
|
||||
|
||||
def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term,
|
||||
def_delegators "@symbols_resolver", :symbols, :nterms, :terms, :add_nterm, :add_term, :find_term_by_s_value,
|
||||
:find_symbol_by_number!, :find_symbol_by_id!, :token_to_symbol,
|
||||
:find_symbol_by_s_value!, :fill_symbol_number, :fill_nterm_type,
|
||||
:fill_printer, :fill_destructor, :fill_error_token, :sort_by_number!
|
||||
|
||||
|
||||
def initialize(rule_counter)
|
||||
def initialize(rule_counter, define = {})
|
||||
@rule_counter = rule_counter
|
||||
|
||||
# Code defined by "%code"
|
||||
@@ -59,10 +56,16 @@ module Lrama
|
||||
@accept_symbol = nil
|
||||
@aux = Auxiliary.new
|
||||
@no_stdlib = false
|
||||
@locations = false
|
||||
@define = define.map {|d| d.split('=') }.to_h
|
||||
|
||||
append_special_symbols
|
||||
end
|
||||
|
||||
def create_rule_builder(rule_counter, midrule_action_counter)
|
||||
RuleBuilder.new(rule_counter, midrule_action_counter, @parameterizing_rule_resolver)
|
||||
end
|
||||
|
||||
def add_percent_code(id:, code:)
|
||||
@percent_codes << PercentCode.new(id.s_value, code.s_value)
|
||||
end
|
||||
@@ -141,6 +144,7 @@ module Lrama
|
||||
end
|
||||
|
||||
def prepare
|
||||
resolve_inline_rules
|
||||
normalize_rules
|
||||
collect_symbols
|
||||
set_lhs_and_rhs
|
||||
@@ -149,6 +153,7 @@ module Lrama
|
||||
fill_sym_to_rules
|
||||
compute_nullable
|
||||
compute_first_set
|
||||
set_locations
|
||||
end
|
||||
|
||||
# TODO: More validation methods
|
||||
@@ -167,6 +172,10 @@ module Lrama
|
||||
@sym_to_rules[sym.number]
|
||||
end
|
||||
|
||||
def ielr_defined?
|
||||
@define.key?('lr.type') && @define['lr.type'] == 'ielr'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def compute_nullable
|
||||
@@ -255,7 +264,7 @@ module Lrama
|
||||
|
||||
def setup_rules
|
||||
@rule_builders.each do |builder|
|
||||
builder.setup_rules(@parameterizing_rule_resolver)
|
||||
builder.setup_rules
|
||||
end
|
||||
end
|
||||
|
||||
@@ -289,10 +298,23 @@ module Lrama
|
||||
@accept_symbol = term
|
||||
end
|
||||
|
||||
def resolve_inline_rules
|
||||
while @rule_builders.any?(&:has_inline_rules?) do
|
||||
@rule_builders = @rule_builders.flat_map do |builder|
|
||||
if builder.has_inline_rules?
|
||||
builder.resolve_inline_rules
|
||||
else
|
||||
builder
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_rules
|
||||
# Add $accept rule to the top of rules
|
||||
lineno = @rule_builders.first ? @rule_builders.first.line : 0
|
||||
@rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [@rule_builders.first.lhs, @eof_symbol.id], token_code: nil, lineno: lineno)
|
||||
rule_builder = @rule_builders.first # : RuleBuilder
|
||||
lineno = rule_builder ? rule_builder.line : 0
|
||||
@rules << Rule.new(id: @rule_counter.increment, _lhs: @accept_symbol.id, _rhs: [rule_builder.lhs, @eof_symbol.id], token_code: nil, lineno: lineno)
|
||||
|
||||
setup_rules
|
||||
|
||||
@@ -365,17 +387,21 @@ module Lrama
|
||||
end
|
||||
|
||||
def validate_rule_lhs_is_nterm!
|
||||
errors = []
|
||||
errors = [] #: Array[String]
|
||||
|
||||
rules.each do |rule|
|
||||
next if rule.lhs.nterm?
|
||||
|
||||
errors << "[BUG] LHS of #{rule} (line: #{rule.lineno}) is term. It should be nterm."
|
||||
errors << "[BUG] LHS of #{rule.display_name} (line: #{rule.lineno}) is term. It should be nterm."
|
||||
end
|
||||
|
||||
return if errors.empty?
|
||||
|
||||
raise errors.join("\n")
|
||||
end
|
||||
|
||||
def set_locations
|
||||
@locations = @locations || @rules.any? {|rule| rule.contains_at_reference? }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
# Grammar file information not used by States but by Output
|
||||
|
||||
@@ -1,22 +1,65 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Binding
|
||||
attr_reader :actual_args, :count
|
||||
# @rbs @actual_args: Array[Lexer::Token]
|
||||
# @rbs @param_to_arg: Hash[String, Lexer::Token]
|
||||
|
||||
def initialize(parameterizing_rule, actual_args)
|
||||
@parameters = parameterizing_rule.parameters
|
||||
# @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> void
|
||||
def initialize(params, actual_args)
|
||||
@actual_args = actual_args
|
||||
@parameter_to_arg = @parameters.zip(actual_args).map do |param, arg|
|
||||
@param_to_arg = map_params_to_args(params, @actual_args)
|
||||
end
|
||||
|
||||
# @rbs (Lexer::Token sym) -> Lexer::Token
|
||||
def resolve_symbol(sym)
|
||||
if sym.is_a?(Lexer::Token::InstantiateRule)
|
||||
Lrama::Lexer::Token::InstantiateRule.new(
|
||||
s_value: sym.s_value, location: sym.location, args: resolved_args(sym), lhs_tag: sym.lhs_tag
|
||||
)
|
||||
else
|
||||
param_to_arg(sym)
|
||||
end
|
||||
end
|
||||
|
||||
# @rbs (Lexer::Token::InstantiateRule token) -> String
|
||||
def concatenated_args_str(token)
|
||||
"#{token.rule_name}_#{token_to_args_s_values(token).join('_')}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @rbs (Array[Lexer::Token] params, Array[Lexer::Token] actual_args) -> Hash[String, Lexer::Token]
|
||||
def map_params_to_args(params, actual_args)
|
||||
params.zip(actual_args).map do |param, arg|
|
||||
[param.s_value, arg]
|
||||
end.to_h
|
||||
end
|
||||
|
||||
def resolve_symbol(symbol)
|
||||
if symbol.is_a?(Lexer::Token::InstantiateRule)
|
||||
resolved_args = symbol.args.map { |arg| resolve_symbol(arg) }
|
||||
Lrama::Lexer::Token::InstantiateRule.new(s_value: symbol.s_value, location: symbol.location, args: resolved_args, lhs_tag: symbol.lhs_tag)
|
||||
else
|
||||
@parameter_to_arg[symbol.s_value] || symbol
|
||||
# @rbs (Lexer::Token::InstantiateRule sym) -> Array[Lexer::Token]
|
||||
def resolved_args(sym)
|
||||
sym.args.map { |arg| resolve_symbol(arg) }
|
||||
end
|
||||
|
||||
# @rbs (Lexer::Token sym) -> Lexer::Token
|
||||
def param_to_arg(sym)
|
||||
if (arg = @param_to_arg[sym.s_value].dup)
|
||||
arg.alias_name = sym.alias_name
|
||||
end
|
||||
arg || sym
|
||||
end
|
||||
|
||||
# @rbs (Lexer::Token::InstantiateRule token) -> Array[String]
|
||||
def token_to_args_s_values(token)
|
||||
token.args.flat_map do |arg|
|
||||
resolved = resolve_symbol(arg)
|
||||
if resolved.is_a?(Lexer::Token::InstantiateRule)
|
||||
[resolved.s_value] + resolved.args.map(&:s_value)
|
||||
else
|
||||
[resolved.s_value]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "forwardable"
|
||||
require "lrama/grammar/code/destructor_code"
|
||||
require "lrama/grammar/code/initial_action_code"
|
||||
require "lrama/grammar/code/no_reference_code"
|
||||
require "lrama/grammar/code/printer_code"
|
||||
require "lrama/grammar/code/rule_action"
|
||||
require_relative "code/destructor_code"
|
||||
require_relative "code/initial_action_code"
|
||||
require_relative "code/no_reference_code"
|
||||
require_relative "code/printer_code"
|
||||
require_relative "code/rule_action"
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Code
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Code
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Code
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Code
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Code
|
||||
@@ -41,6 +43,7 @@ module Lrama
|
||||
when ref.type == :dollar && ref.name == "$" # $$
|
||||
tag = ref.ex_tag || lhs.tag
|
||||
raise_tag_not_found_error(ref) unless tag
|
||||
# @type var tag: Lexer::Token::Tag
|
||||
"(yyval.#{tag.member})"
|
||||
when ref.type == :at && ref.name == "$" # @$
|
||||
"(yyloc)"
|
||||
@@ -50,6 +53,7 @@ module Lrama
|
||||
i = -position_in_rhs + ref.index
|
||||
tag = ref.ex_tag || rhs[ref.index - 1].tag
|
||||
raise_tag_not_found_error(ref) unless tag
|
||||
# @type var tag: Lexer::Token::Tag
|
||||
"(yyvsp[#{i}].#{tag.member})"
|
||||
when ref.type == :at # @n
|
||||
i = -position_in_rhs + ref.index
|
||||
@@ -69,18 +73,18 @@ module Lrama
|
||||
@rule.position_in_original_rule_rhs || @rule.rhs.count
|
||||
end
|
||||
|
||||
# If this is midrule action, RHS is a RHS of the original rule.
|
||||
# If this is midrule action, RHS is an RHS of the original rule.
|
||||
def rhs
|
||||
(@rule.original_rule || @rule).rhs
|
||||
end
|
||||
|
||||
# Unlike `rhs`, LHS is always a LHS of the rule.
|
||||
# Unlike `rhs`, LHS is always an LHS of the rule.
|
||||
def lhs
|
||||
@rule.lhs
|
||||
end
|
||||
|
||||
def raise_tag_not_found_error(ref)
|
||||
raise "Tag is not specified for '$#{ref.value}' in '#{@rule}'"
|
||||
raise "Tag is not specified for '$#{ref.value}' in '#{@rule.display_name}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Counter
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Destructor < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class ErrorToken < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'parameterizing_rule/resolver'
|
||||
require_relative 'parameterizing_rule/rhs'
|
||||
require_relative 'parameterizing_rule/rule'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class ParameterizingRule
|
||||
@@ -18,13 +20,17 @@ module Lrama
|
||||
end
|
||||
|
||||
def find_inline(token)
|
||||
@rules.select { |rule| rule.name == token.s_value && rule.is_inline }.last
|
||||
@rules.reverse.find { |rule| rule.name == token.s_value && rule.is_inline }
|
||||
end
|
||||
|
||||
def created_lhs(lhs_s_value)
|
||||
@created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value }
|
||||
end
|
||||
|
||||
def redefined_rules
|
||||
@rules.select { |rule| @rules.count { |r| r.name == rule.name && r.required_parameters_count == rule.required_parameters_count } > 1 }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def select_rules(rules, token)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class ParameterizingRule
|
||||
@@ -13,7 +15,8 @@ module Lrama
|
||||
def resolve_user_code(bindings)
|
||||
return unless user_code
|
||||
|
||||
var_to_arg = {}
|
||||
resolved = Lexer::Token::UserCode.new(s_value: user_code.s_value, location: user_code.location)
|
||||
var_to_arg = {} #: Hash[String, String]
|
||||
symbols.each do |sym|
|
||||
resolved_sym = bindings.resolve_symbol(sym)
|
||||
if resolved_sym != sym
|
||||
@@ -22,14 +25,14 @@ module Lrama
|
||||
end
|
||||
|
||||
var_to_arg.each do |var, arg|
|
||||
user_code.references.each do |ref|
|
||||
resolved.references.each do |ref|
|
||||
if ref.name == var
|
||||
ref.name = arg
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return user_code
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class ParameterizingRule
|
||||
@@ -12,6 +14,10 @@ module Lrama
|
||||
@is_inline = is_inline
|
||||
@required_parameters_count = parameters.count
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{@name}(#{@parameters.map(&:s_value).join(', ')})"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class PercentCode
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Precedence < Struct.new(:type, :precedence, keyword_init: true)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Printer < Struct.new(:ident_or_tags, :token_code, :lineno, keyword_init: true)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
# type: :dollar or :at
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
# _rhs holds original RHS element. Use rhs to refer to Symbol.
|
||||
@@ -16,10 +18,17 @@ module Lrama
|
||||
self.lineno == other.lineno
|
||||
end
|
||||
|
||||
# TODO: Change this to display_name
|
||||
def to_s
|
||||
def display_name
|
||||
l = lhs.id.s_value
|
||||
r = empty_rule? ? "ε" : rhs.map {|r| r.id.s_value }.join(" ")
|
||||
"#{l} -> #{r}"
|
||||
end
|
||||
|
||||
def display_name_without_action
|
||||
l = lhs.id.s_value
|
||||
r = empty_rule? ? "ε" : rhs.map do |r|
|
||||
r.id.s_value if r.first_set.any?
|
||||
end.compact.join(" ")
|
||||
|
||||
"#{l} -> #{r}"
|
||||
end
|
||||
@@ -33,7 +42,7 @@ module Lrama
|
||||
end
|
||||
|
||||
def with_actions
|
||||
"#{to_s} {#{token_code&.s_value}}"
|
||||
"#{display_name} {#{token_code&.s_value}}"
|
||||
end
|
||||
|
||||
# opt_nl: ε <-- empty_rule
|
||||
@@ -55,6 +64,12 @@ module Lrama
|
||||
|
||||
Code::RuleAction.new(type: :rule_action, token_code: token_code, rule: self).translated_code
|
||||
end
|
||||
|
||||
def contains_at_reference?
|
||||
return false unless token_code
|
||||
|
||||
token_code.references.any? {|r| r.type == :at }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class RuleBuilder
|
||||
attr_accessor :lhs, :line
|
||||
attr_reader :lhs_tag, :rhs, :user_code, :precedence_sym
|
||||
|
||||
def initialize(rule_counter, midrule_action_counter, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false)
|
||||
def initialize(rule_counter, midrule_action_counter, parameterizing_rule_resolver, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false)
|
||||
@rule_counter = rule_counter
|
||||
@midrule_action_counter = midrule_action_counter
|
||||
@parameterizing_rule_resolver = parameterizing_rule_resolver
|
||||
@position_in_original_rule_rhs = position_in_original_rule_rhs
|
||||
@skip_preprocess_references = skip_preprocess_references
|
||||
|
||||
@@ -19,16 +22,12 @@ module Lrama
|
||||
@rules = []
|
||||
@rule_builders_for_parameterizing_rules = []
|
||||
@rule_builders_for_derived_rules = []
|
||||
@rule_builders_for_inline_rules = []
|
||||
@parameterizing_rules = []
|
||||
@inline_rules = []
|
||||
@midrule_action_rules = []
|
||||
end
|
||||
|
||||
def add_rhs(rhs)
|
||||
if !@line
|
||||
@line = rhs.line
|
||||
end
|
||||
@line ||= rhs.line
|
||||
|
||||
flush_user_code
|
||||
|
||||
@@ -36,9 +35,7 @@ module Lrama
|
||||
end
|
||||
|
||||
def user_code=(user_code)
|
||||
if !@line
|
||||
@line = user_code&.line
|
||||
end
|
||||
@line ||= user_code&.line
|
||||
|
||||
flush_user_code
|
||||
|
||||
@@ -55,18 +52,41 @@ module Lrama
|
||||
freeze_rhs
|
||||
end
|
||||
|
||||
def setup_rules(parameterizing_rule_resolver)
|
||||
def setup_rules
|
||||
preprocess_references unless @skip_preprocess_references
|
||||
if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) }
|
||||
resolve_inline(parameterizing_rule_resolver)
|
||||
else
|
||||
process_rhs(parameterizing_rule_resolver)
|
||||
end
|
||||
process_rhs
|
||||
build_rules
|
||||
end
|
||||
|
||||
def rules
|
||||
@parameterizing_rules + @inline_rules + @midrule_action_rules + @rules
|
||||
@parameterizing_rules + @midrule_action_rules + @rules
|
||||
end
|
||||
|
||||
def has_inline_rules?
|
||||
rhs.any? { |token| @parameterizing_rule_resolver.find_inline(token) }
|
||||
end
|
||||
|
||||
def resolve_inline_rules
|
||||
resolved_builders = [] #: Array[RuleBuilder]
|
||||
rhs.each_with_index do |token, i|
|
||||
if (inline_rule = @parameterizing_rule_resolver.find_inline(token))
|
||||
inline_rule.rhs_list.each do |inline_rhs|
|
||||
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: lhs_tag)
|
||||
if token.is_a?(Lexer::Token::InstantiateRule)
|
||||
resolve_inline_rhs(rule_builder, inline_rhs, i, Binding.new(inline_rule.parameters, token.args))
|
||||
else
|
||||
resolve_inline_rhs(rule_builder, inline_rhs, i)
|
||||
end
|
||||
rule_builder.lhs = lhs
|
||||
rule_builder.line = line
|
||||
rule_builder.precedence_sym = precedence_sym
|
||||
rule_builder.user_code = replace_inline_user_code(inline_rhs, i)
|
||||
resolved_builders << rule_builder
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
resolved_builders
|
||||
end
|
||||
|
||||
private
|
||||
@@ -82,31 +102,25 @@ module Lrama
|
||||
def build_rules
|
||||
tokens = @replaced_rhs
|
||||
|
||||
if tokens
|
||||
rule = Rule.new(
|
||||
id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code,
|
||||
position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line
|
||||
)
|
||||
@rules = [rule]
|
||||
@parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder|
|
||||
rule_builder.rules
|
||||
end.flatten
|
||||
@midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder|
|
||||
rule_builder.rules
|
||||
end.flatten
|
||||
@midrule_action_rules.each do |r|
|
||||
r.original_rule = rule
|
||||
end
|
||||
else
|
||||
@inline_rules = @rule_builders_for_inline_rules.map do |rule_builder|
|
||||
rule_builder.rules
|
||||
end.flatten
|
||||
rule = Rule.new(
|
||||
id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code,
|
||||
position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line
|
||||
)
|
||||
@rules = [rule]
|
||||
@parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder|
|
||||
rule_builder.rules
|
||||
end.flatten
|
||||
@midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder|
|
||||
rule_builder.rules
|
||||
end.flatten
|
||||
@midrule_action_rules.each do |r|
|
||||
r.original_rule = rule
|
||||
end
|
||||
end
|
||||
|
||||
# rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on.
|
||||
# `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`.
|
||||
def process_rhs(parameterizing_rule_resolver)
|
||||
def process_rhs
|
||||
return if @replaced_rhs
|
||||
|
||||
@replaced_rhs = []
|
||||
@@ -118,26 +132,26 @@ module Lrama
|
||||
when Lrama::Lexer::Token::Ident
|
||||
@replaced_rhs << token
|
||||
when Lrama::Lexer::Token::InstantiateRule
|
||||
parameterizing_rule = parameterizing_rule_resolver.find_rule(token)
|
||||
parameterizing_rule = @parameterizing_rule_resolver.find_rule(token)
|
||||
raise "Unexpected token. #{token}" unless parameterizing_rule
|
||||
|
||||
bindings = Binding.new(parameterizing_rule, token.args)
|
||||
lhs_s_value = lhs_s_value(token, bindings)
|
||||
if (created_lhs = parameterizing_rule_resolver.created_lhs(lhs_s_value))
|
||||
bindings = Binding.new(parameterizing_rule.parameters, token.args)
|
||||
lhs_s_value = bindings.concatenated_args_str(token)
|
||||
if (created_lhs = @parameterizing_rule_resolver.created_lhs(lhs_s_value))
|
||||
@replaced_rhs << created_lhs
|
||||
else
|
||||
lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location)
|
||||
@replaced_rhs << lhs_token
|
||||
parameterizing_rule_resolver.created_lhs_list << lhs_token
|
||||
@parameterizing_rule_resolver.created_lhs_list << lhs_token
|
||||
parameterizing_rule.rhs_list.each do |r|
|
||||
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: token.lhs_tag || parameterizing_rule.tag)
|
||||
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: token.lhs_tag || parameterizing_rule.tag)
|
||||
rule_builder.lhs = lhs_token
|
||||
r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) }
|
||||
rule_builder.line = line
|
||||
rule_builder.precedence_sym = r.precedence_sym
|
||||
rule_builder.user_code = r.resolve_user_code(bindings)
|
||||
rule_builder.complete_input
|
||||
rule_builder.setup_rules(parameterizing_rule_resolver)
|
||||
rule_builder.setup_rules
|
||||
@rule_builders_for_parameterizing_rules << rule_builder
|
||||
end
|
||||
end
|
||||
@@ -147,11 +161,11 @@ module Lrama
|
||||
new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s)
|
||||
@replaced_rhs << new_token
|
||||
|
||||
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, i, lhs_tag: tag, skip_preprocess_references: true)
|
||||
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, i, lhs_tag: tag, skip_preprocess_references: true)
|
||||
rule_builder.lhs = new_token
|
||||
rule_builder.user_code = token
|
||||
rule_builder.complete_input
|
||||
rule_builder.setup_rules(parameterizing_rule_resolver)
|
||||
rule_builder.setup_rules
|
||||
|
||||
@rule_builders_for_derived_rules << rule_builder
|
||||
else
|
||||
@@ -160,39 +174,10 @@ module Lrama
|
||||
end
|
||||
end
|
||||
|
||||
def lhs_s_value(token, bindings)
|
||||
s_values = token.args.map do |arg|
|
||||
resolved = bindings.resolve_symbol(arg)
|
||||
if resolved.is_a?(Lexer::Token::InstantiateRule)
|
||||
[resolved.s_value, resolved.args.map(&:s_value)]
|
||||
else
|
||||
resolved.s_value
|
||||
end
|
||||
end
|
||||
"#{token.rule_name}_#{s_values.join('_')}"
|
||||
end
|
||||
|
||||
def resolve_inline(parameterizing_rule_resolver)
|
||||
rhs.each_with_index do |token, i|
|
||||
if inline_rule = parameterizing_rule_resolver.find_inline(token)
|
||||
inline_rule.rhs_list.each_with_index do |inline_rhs|
|
||||
rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: lhs_tag, skip_preprocess_references: true)
|
||||
resolve_inline_rhs(rule_builder, inline_rhs, i)
|
||||
rule_builder.lhs = lhs
|
||||
rule_builder.line = line
|
||||
rule_builder.user_code = replace_inline_user_code(inline_rhs, i)
|
||||
rule_builder.complete_input
|
||||
rule_builder.setup_rules(parameterizing_rule_resolver)
|
||||
@rule_builders_for_inline_rules << rule_builder
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_inline_rhs(rule_builder, inline_rhs, index)
|
||||
def resolve_inline_rhs(rule_builder, inline_rhs, index, bindings = nil)
|
||||
rhs.each_with_index do |token, i|
|
||||
if index == i
|
||||
inline_rhs.symbols.each { |sym| rule_builder.add_rhs(sym) }
|
||||
inline_rhs.symbols.each { |sym| rule_builder.add_rhs(bindings.nil? ? sym : bindings.resolve_symbol(sym)) }
|
||||
else
|
||||
rule_builder.add_rhs(token)
|
||||
end
|
||||
@@ -204,6 +189,11 @@ module Lrama
|
||||
return user_code if user_code.nil?
|
||||
|
||||
code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value)
|
||||
user_code.references.each do |ref|
|
||||
next if ref.index.nil? || ref.index <= index # nil is a case for `$$`
|
||||
code = code.gsub(/\$#{ref.index}/, "$#{ref.index + (inline_rhs.symbols.count-1)}")
|
||||
code = code.gsub(/@#{ref.index}/, "@#{ref.index + (inline_rhs.symbols.count-1)}")
|
||||
end
|
||||
Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location)
|
||||
end
|
||||
|
||||
@@ -238,9 +228,6 @@ module Lrama
|
||||
end
|
||||
|
||||
if ref.number
|
||||
# TODO: When Inlining is implemented, for example, if `$1` is expanded to multiple RHS tokens,
|
||||
# `$2` needs to access `$2 + n` to actually access it. So, after the Inlining implementation,
|
||||
# it needs resolves from number to index.
|
||||
ref.index = ref.number
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Symbol is both of nterm and term
|
||||
# `number` is both for nterm and term
|
||||
# `token_id` is tokentype for term, internal sequence number for nterm
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "symbols/resolver"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Symbols
|
||||
@@ -42,7 +44,9 @@ module Lrama
|
||||
end
|
||||
|
||||
def add_nterm(id:, alias_name: nil, tag: nil)
|
||||
return if find_symbol_by_id(id)
|
||||
if (sym = find_symbol_by_id(id))
|
||||
return sym
|
||||
end
|
||||
|
||||
@symbols = nil
|
||||
nterm = Symbol.new(
|
||||
@@ -53,6 +57,10 @@ module Lrama
|
||||
nterm
|
||||
end
|
||||
|
||||
def find_term_by_s_value(s_value)
|
||||
terms.find { |s| s.id.s_value == s_value }
|
||||
end
|
||||
|
||||
def find_symbol_by_s_value(s_value)
|
||||
symbols.find { |s| s.id.s_value == s_value }
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Type
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Grammar
|
||||
class Union < Struct.new(:code, :lineno, keyword_init: true)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class GrammarValidator
|
||||
def initialize(grammar, states, logger)
|
||||
@grammar = grammar
|
||||
@states = states
|
||||
@logger = logger
|
||||
end
|
||||
|
||||
def valid?
|
||||
conflicts_within_threshold?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def conflicts_within_threshold?
|
||||
return true unless @grammar.expect
|
||||
|
||||
[sr_conflicts_within_threshold(@grammar.expect), rr_conflicts_within_threshold(0)].all?
|
||||
end
|
||||
|
||||
def sr_conflicts_within_threshold(expected)
|
||||
return true if expected == @states.sr_conflicts_count
|
||||
|
||||
@logger.error("shift/reduce conflicts: #{@states.sr_conflicts_count} found, #{expected} expected")
|
||||
false
|
||||
end
|
||||
|
||||
def rr_conflicts_within_threshold(expected)
|
||||
return true if expected == @states.rr_conflicts_count
|
||||
|
||||
@logger.error("reduce/reduce conflicts: #{@states.rr_conflicts_count} found, #{expected} expected")
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,19 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "strscan"
|
||||
|
||||
require "lrama/lexer/grammar_file"
|
||||
require "lrama/lexer/location"
|
||||
require "lrama/lexer/token"
|
||||
require_relative "lexer/grammar_file"
|
||||
require_relative "lexer/location"
|
||||
require_relative "lexer/token"
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
attr_reader :head_line, :head_column, :line
|
||||
attr_accessor :status, :end_symbol
|
||||
|
||||
SYMBOLS = ['%{', '%}', '%%', '{', '}', '\[', '\]', '\(', '\)', '\,', ':', '\|', ';']
|
||||
SYMBOLS = ['%{', '%}', '%%', '{', '}', '\[', '\]', '\(', '\)', '\,', ':', '\|', ';'].freeze
|
||||
PERCENT_TOKENS = %w(
|
||||
%union
|
||||
%token
|
||||
%type
|
||||
%nterm
|
||||
%left
|
||||
%right
|
||||
%nonassoc
|
||||
@@ -38,7 +41,8 @@ module Lrama
|
||||
%rule
|
||||
%no-stdlib
|
||||
%inline
|
||||
)
|
||||
%locations
|
||||
).freeze
|
||||
|
||||
def initialize(grammar_file)
|
||||
@grammar_file = grammar_file
|
||||
@@ -71,7 +75,7 @@ module Lrama
|
||||
end
|
||||
|
||||
def lex_token
|
||||
while !@scanner.eos? do
|
||||
until @scanner.eos? do
|
||||
case
|
||||
when @scanner.scan(/\n/)
|
||||
newline
|
||||
@@ -126,7 +130,7 @@ module Lrama
|
||||
code = ''
|
||||
reset_first_position
|
||||
|
||||
while !@scanner.eos? do
|
||||
until @scanner.eos? do
|
||||
case
|
||||
when @scanner.scan(/{/)
|
||||
code += @scanner.matched
|
||||
@@ -163,14 +167,13 @@ module Lrama
|
||||
private
|
||||
|
||||
def lex_comment
|
||||
while !@scanner.eos? do
|
||||
until @scanner.eos? do
|
||||
case
|
||||
when @scanner.scan(/\n/)
|
||||
newline
|
||||
when @scanner.scan(/\*\//)
|
||||
when @scanner.scan_until(/[\s\S]*?\*\//)
|
||||
@scanner.matched.count("\n").times { newline }
|
||||
return
|
||||
else
|
||||
@scanner.getch
|
||||
when @scanner.scan_until(/\n/)
|
||||
newline
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,28 +1,37 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class GrammarFile
|
||||
class Text < String
|
||||
# @rbs () -> String
|
||||
def inspect
|
||||
length <= 50 ? super : "#{self[0..47]}...".inspect
|
||||
end
|
||||
end
|
||||
|
||||
attr_reader :path, :text
|
||||
attr_reader :path #: String
|
||||
attr_reader :text #: String
|
||||
|
||||
# @rbs (String path, String text) -> void
|
||||
def initialize(path, text)
|
||||
@path = path
|
||||
@text = Text.new(text).freeze
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def inspect
|
||||
"<#{self.class}: @path=#{path}, @text=#{text.inspect}>"
|
||||
end
|
||||
|
||||
# @rbs (GrammarFile other) -> bool
|
||||
def ==(other)
|
||||
self.class == other.class &&
|
||||
self.path == other.path
|
||||
end
|
||||
|
||||
# @rbs () -> Array[String]
|
||||
def lines
|
||||
@lines ||= text.split("\n")
|
||||
end
|
||||
|
||||
@@ -1,8 +1,16 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Location
|
||||
attr_reader :grammar_file, :first_line, :first_column, :last_line, :last_column
|
||||
attr_reader :grammar_file #: GrammarFile
|
||||
attr_reader :first_line #: Integer
|
||||
attr_reader :first_column #: Integer
|
||||
attr_reader :last_line #: Integer
|
||||
attr_reader :last_column #: Integer
|
||||
|
||||
# @rbs (grammar_file: GrammarFile, first_line: Integer, first_column: Integer, last_line: Integer, last_column: Integer) -> void
|
||||
def initialize(grammar_file:, first_line:, first_column:, last_line:, last_column:)
|
||||
@grammar_file = grammar_file
|
||||
@first_line = first_line
|
||||
@@ -11,6 +19,7 @@ module Lrama
|
||||
@last_column = last_column
|
||||
end
|
||||
|
||||
# @rbs (Location other) -> bool
|
||||
def ==(other)
|
||||
self.class == other.class &&
|
||||
self.grammar_file == other.grammar_file &&
|
||||
@@ -20,6 +29,7 @@ module Lrama
|
||||
self.last_column == other.last_column
|
||||
end
|
||||
|
||||
# @rbs (Integer left, Integer right) -> Location
|
||||
def partial_location(left, right)
|
||||
offset = -first_column
|
||||
new_first_line = -1
|
||||
@@ -50,10 +60,12 @@ module Lrama
|
||||
)
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def to_s
|
||||
"#{path} (#{first_line},#{first_column})-(#{last_line},#{last_column})"
|
||||
end
|
||||
|
||||
# @rbs (String error_message) -> String
|
||||
def generate_error_message(error_message)
|
||||
<<~ERROR.chomp
|
||||
#{path}:#{first_line}:#{first_column}: #{error_message}
|
||||
@@ -61,6 +73,7 @@ module Lrama
|
||||
ERROR
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def line_with_carets
|
||||
<<~TEXT
|
||||
#{text}
|
||||
@@ -70,22 +83,27 @@ module Lrama
|
||||
|
||||
private
|
||||
|
||||
# @rbs () -> String
|
||||
def path
|
||||
grammar_file.path
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def blanks
|
||||
(text[0...first_column] or raise "#{first_column} is invalid").gsub(/[^\t]/, ' ')
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def carets
|
||||
blanks + '^' * (last_column - first_column)
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def text
|
||||
@text ||= _text.join("\n")
|
||||
end
|
||||
|
||||
# @rbs () -> Array[String]
|
||||
def _text
|
||||
@_text ||=begin
|
||||
range = (first_line - 1)...last_line
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
require 'lrama/lexer/token/char'
|
||||
require 'lrama/lexer/token/ident'
|
||||
require 'lrama/lexer/token/instantiate_rule'
|
||||
require 'lrama/lexer/token/tag'
|
||||
require 'lrama/lexer/token/user_code'
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'token/char'
|
||||
require_relative 'token/ident'
|
||||
require_relative 'token/instantiate_rule'
|
||||
require_relative 'token/tag'
|
||||
require_relative 'token/user_code'
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Token
|
||||
attr_reader :s_value, :location
|
||||
attr_accessor :alias_name, :referred
|
||||
attr_reader :s_value #: String
|
||||
attr_reader :location #: Location
|
||||
attr_accessor :alias_name #: String
|
||||
attr_accessor :referred #: bool
|
||||
|
||||
# @rbs (s_value: String, ?alias_name: String, ?location: Location) -> void
|
||||
def initialize(s_value:, alias_name: nil, location: nil)
|
||||
s_value.freeze
|
||||
@s_value = s_value
|
||||
@@ -17,36 +23,44 @@ module Lrama
|
||||
@location = location
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def to_s
|
||||
"value: `#{s_value}`, location: #{location}"
|
||||
end
|
||||
|
||||
# @rbs (String string) -> bool
|
||||
def referred_by?(string)
|
||||
[self.s_value, self.alias_name].compact.include?(string)
|
||||
end
|
||||
|
||||
# @rbs (Token other) -> bool
|
||||
def ==(other)
|
||||
self.class == other.class && self.s_value == other.s_value
|
||||
end
|
||||
|
||||
# @rbs () -> Integer
|
||||
def first_line
|
||||
location.first_line
|
||||
end
|
||||
alias :line :first_line
|
||||
|
||||
# @rbs () -> Integer
|
||||
def first_column
|
||||
location.first_column
|
||||
end
|
||||
alias :column :first_column
|
||||
|
||||
# @rbs () -> Integer
|
||||
def last_line
|
||||
location.last_line
|
||||
end
|
||||
|
||||
# @rbs () -> Integer
|
||||
def last_column
|
||||
location.last_column
|
||||
end
|
||||
|
||||
# @rbs (Lrama::Grammar::Reference ref, String message) -> bot
|
||||
def invalid_ref(ref, message)
|
||||
location = self.location.partial_location(ref.first_column, ref.last_column)
|
||||
raise location.generate_error_message(message)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Token
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Token
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Token
|
||||
class InstantiateRule < Token
|
||||
attr_reader :args, :lhs_tag
|
||||
attr_reader :args #: Array[Lexer::Token]
|
||||
attr_reader :lhs_tag #: Lexer::Token::Tag?
|
||||
|
||||
# @rbs (s_value: String, ?alias_name: String, ?location: Location, ?args: Array[Lexer::Token], ?lhs_tag: Lexer::Token::Tag?) -> void
|
||||
def initialize(s_value:, alias_name: nil, location: nil, args: [], lhs_tag: nil)
|
||||
super s_value: s_value, alias_name: alias_name, location: location
|
||||
@args = args
|
||||
@lhs_tag = lhs_tag
|
||||
end
|
||||
|
||||
# @rbs () -> String
|
||||
def rule_name
|
||||
s_value
|
||||
end
|
||||
|
||||
# @rbs () -> Integer
|
||||
def args_count
|
||||
args.count
|
||||
end
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Token
|
||||
class Tag < Token
|
||||
# Omit "<>"
|
||||
# @rbs () -> String
|
||||
def member
|
||||
# Omit "<>"
|
||||
s_value[1..-2] or raise "Unexpected Tag format (#{s_value})"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,22 +1,27 @@
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "strscan"
|
||||
|
||||
module Lrama
|
||||
class Lexer
|
||||
class Token
|
||||
class UserCode < Token
|
||||
attr_accessor :tag
|
||||
attr_accessor :tag #: Lexer::Token::Tag
|
||||
|
||||
# @rbs () -> Array[Lrama::Grammar::Reference]
|
||||
def references
|
||||
@references ||= _references
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @rbs () -> Array[Lrama::Grammar::Reference]
|
||||
def _references
|
||||
scanner = StringScanner.new(s_value)
|
||||
references = []
|
||||
references = [] #: Array[Grammar::Reference]
|
||||
|
||||
while !scanner.eos? do
|
||||
until scanner.eos? do
|
||||
case
|
||||
when reference = scan_reference(scanner)
|
||||
references << reference
|
||||
@@ -30,6 +35,7 @@ module Lrama
|
||||
references
|
||||
end
|
||||
|
||||
# @rbs (StringScanner scanner) -> Lrama::Grammar::Reference?
|
||||
def scan_reference(scanner)
|
||||
start = scanner.pos
|
||||
case
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
module Lrama
|
||||
class Warning
|
||||
attr_reader :errors, :warns
|
||||
# rbs_inline: enabled
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Logger
|
||||
# @rbs (IO out) -> void
|
||||
def initialize(out = STDERR)
|
||||
@out = out
|
||||
@errors = []
|
||||
@warns = []
|
||||
end
|
||||
|
||||
def error(message)
|
||||
@out << message << "\n"
|
||||
@errors << message
|
||||
end
|
||||
|
||||
# @rbs (String message) -> void
|
||||
def warn(message)
|
||||
@out << message << "\n"
|
||||
@warns << message
|
||||
end
|
||||
|
||||
def has_error?
|
||||
!@errors.empty?
|
||||
# @rbs (String message) -> void
|
||||
def error(message)
|
||||
@out << message << "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'optparse'
|
||||
|
||||
module Lrama
|
||||
@@ -16,7 +18,7 @@ module Lrama
|
||||
@options.report_opts = validate_report(@report)
|
||||
@options.grammar_file = argv.shift
|
||||
|
||||
if !@options.grammar_file
|
||||
unless @options.grammar_file
|
||||
abort "File should be specified\n"
|
||||
end
|
||||
|
||||
@@ -57,26 +59,42 @@ module Lrama
|
||||
o.separator ''
|
||||
o.separator 'Tuning the Parser:'
|
||||
o.on('-S', '--skeleton=FILE', 'specify the skeleton to use') {|v| @options.skeleton = v }
|
||||
o.on('-t', 'reserved, do nothing') { }
|
||||
o.on('--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true }
|
||||
o.on('-t', '--debug', 'display debugging outputs of internal parser') {|v| @options.debug = true }
|
||||
o.on('-D', '--define=NAME[=VALUE]', Array, "similar to '%define NAME VALUE'") {|v| @options.define = v }
|
||||
o.separator ''
|
||||
o.separator 'Output:'
|
||||
o.on('-H', '--header=[FILE]', 'also produce a header file named FILE') {|v| @options.header = true; @options.header_file = v }
|
||||
o.on('-d', 'also produce a header file') { @options.header = true }
|
||||
o.on('-r', '--report=THINGS', Array, 'also produce details on the automaton') {|v| @report = v }
|
||||
o.on('-r', '--report=REPORTS', Array, 'also produce details on the automaton') {|v| @report = v }
|
||||
o.on_tail ''
|
||||
o.on_tail 'Valid Reports:'
|
||||
o.on_tail " #{VALID_REPORTS.join(' ')}"
|
||||
|
||||
o.on_tail 'REPORTS is a list of comma-separated words that can include:'
|
||||
o.on_tail ' states describe the states'
|
||||
o.on_tail ' itemsets complete the core item sets with their closure'
|
||||
o.on_tail ' lookaheads explicitly associate lookahead tokens to items'
|
||||
o.on_tail ' solved describe shift/reduce conflicts solving'
|
||||
o.on_tail ' counterexamples, cex generate conflict counterexamples'
|
||||
o.on_tail ' rules list unused rules'
|
||||
o.on_tail ' terms list unused terminals'
|
||||
o.on_tail ' verbose report detailed internal state and analysis results'
|
||||
o.on_tail ' all include all the above reports'
|
||||
o.on_tail ' none disable all reports'
|
||||
o.on('--report-file=FILE', 'also produce details on the automaton output to a file named FILE') {|v| @options.report_file = v }
|
||||
o.on('-o', '--output=FILE', 'leave output to FILE') {|v| @options.outfile = v }
|
||||
|
||||
o.on('--trace=THINGS', Array, 'also output trace logs at runtime') {|v| @trace = v }
|
||||
o.on('--trace=TRACES', Array, 'also output trace logs at runtime') {|v| @trace = v }
|
||||
o.on_tail ''
|
||||
o.on_tail 'Valid Traces:'
|
||||
o.on_tail " #{VALID_TRACES.join(' ')}"
|
||||
|
||||
o.on('-v', 'reserved, do nothing') { }
|
||||
o.on_tail 'TRACES is a list of comma-separated words that can include:'
|
||||
o.on_tail ' automaton display states'
|
||||
o.on_tail ' closure display states'
|
||||
o.on_tail ' rules display grammar rules'
|
||||
o.on_tail ' only-explicit-rules display only explicit grammar rules'
|
||||
o.on_tail ' actions display grammar rules with actions'
|
||||
o.on_tail ' time display generation time'
|
||||
o.on_tail ' all include all the above traces'
|
||||
o.on_tail ' none disable all traces'
|
||||
o.on('-v', '--verbose', "same as '--report=state'") {|_v| @report << 'states' }
|
||||
o.separator ''
|
||||
o.separator 'Diagnostics:'
|
||||
o.on('-W', '--warnings', 'report the warnings') {|v| @options.diagnostic = true }
|
||||
o.separator ''
|
||||
o.separator 'Error Recovery:'
|
||||
o.on('-e', 'enable error recovery') {|v| @options.error_recovery = true }
|
||||
@@ -89,48 +107,57 @@ module Lrama
|
||||
end
|
||||
end
|
||||
|
||||
BISON_REPORTS = %w[states itemsets lookaheads solved counterexamples cex all none]
|
||||
OTHER_REPORTS = %w[verbose]
|
||||
NOT_SUPPORTED_REPORTS = %w[cex none]
|
||||
VALID_REPORTS = BISON_REPORTS + OTHER_REPORTS - NOT_SUPPORTED_REPORTS
|
||||
ALIASED_REPORTS = { cex: :counterexamples }.freeze
|
||||
VALID_REPORTS = %i[states itemsets lookaheads solved counterexamples rules terms verbose].freeze
|
||||
|
||||
def validate_report(report)
|
||||
list = VALID_REPORTS
|
||||
h = { grammar: true }
|
||||
return h if report.empty?
|
||||
return {} if report == ['none']
|
||||
if report == ['all']
|
||||
VALID_REPORTS.each { |r| h[r] = true }
|
||||
return h
|
||||
end
|
||||
|
||||
report.each do |r|
|
||||
if list.include?(r)
|
||||
h[r.to_sym] = true
|
||||
aliased = aliased_report_option(r)
|
||||
if VALID_REPORTS.include?(aliased)
|
||||
h[aliased] = true
|
||||
else
|
||||
raise "Invalid report option \"#{r}\"."
|
||||
end
|
||||
end
|
||||
|
||||
if h[:all]
|
||||
(BISON_REPORTS - NOT_SUPPORTED_REPORTS).each do |r|
|
||||
h[r.to_sym] = true
|
||||
end
|
||||
|
||||
h.delete(:all)
|
||||
end
|
||||
|
||||
return h
|
||||
end
|
||||
|
||||
def aliased_report_option(opt)
|
||||
(ALIASED_REPORTS[opt.to_sym] || opt).to_sym
|
||||
end
|
||||
|
||||
VALID_TRACES = %w[
|
||||
none locations scan parse automaton bitsets
|
||||
closure grammar rules actions resource
|
||||
sets muscles tools m4-early m4 skeleton time
|
||||
ielr cex all
|
||||
]
|
||||
locations scan parse automaton bitsets closure
|
||||
grammar rules only-explicit-rules actions resource
|
||||
sets muscles tools m4-early m4 skeleton time ielr cex
|
||||
].freeze
|
||||
NOT_SUPPORTED_TRACES = %w[
|
||||
locations scan parse bitsets grammar resource
|
||||
sets muscles tools m4-early m4 skeleton ielr cex
|
||||
].freeze
|
||||
SUPPORTED_TRACES = VALID_TRACES - NOT_SUPPORTED_TRACES
|
||||
|
||||
def validate_trace(trace)
|
||||
list = VALID_TRACES
|
||||
h = {}
|
||||
return h if trace.empty? || trace == ['none']
|
||||
all_traces = SUPPORTED_TRACES - %w[only-explicit-rules]
|
||||
if trace == ['all']
|
||||
all_traces.each { |t| h[t.gsub(/-/, '_').to_sym] = true }
|
||||
return h
|
||||
end
|
||||
|
||||
trace.each do |t|
|
||||
if list.include?(t)
|
||||
h[t.to_sym] = true
|
||||
if SUPPORTED_TRACES.include?(t)
|
||||
h[t.gsub(/-/, '_').to_sym] = true
|
||||
else
|
||||
raise "Invalid trace option \"#{t}\"."
|
||||
end
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
# Command line options.
|
||||
class Options
|
||||
attr_accessor :skeleton, :header, :header_file,
|
||||
:report_file, :outfile,
|
||||
:error_recovery, :grammar_file,
|
||||
:trace_opts, :report_opts, :y,
|
||||
:debug
|
||||
:trace_opts, :report_opts,
|
||||
:diagnostic, :y, :debug, :define
|
||||
|
||||
def initialize
|
||||
@skeleton = "bison/yacc.c"
|
||||
@define = {}
|
||||
@header = false
|
||||
@header_file = nil
|
||||
@report_file = nil
|
||||
@@ -17,6 +20,7 @@ module Lrama
|
||||
@grammar_file = nil
|
||||
@trace_opts = nil
|
||||
@report_opts = nil
|
||||
@diagnostic = false
|
||||
@y = STDIN
|
||||
@debug = false
|
||||
end
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "erb"
|
||||
require "forwardable"
|
||||
require "lrama/report/duration"
|
||||
require_relative "report/duration"
|
||||
|
||||
module Lrama
|
||||
class Output
|
||||
@@ -63,37 +65,29 @@ module Lrama
|
||||
|
||||
# A part of b4_token_enums
|
||||
def token_enums
|
||||
str = ""
|
||||
|
||||
@context.yytokentype.each do |s_value, token_id, display_name|
|
||||
@context.yytokentype.map do |s_value, token_id, display_name|
|
||||
s = sprintf("%s = %d%s", s_value, token_id, token_id == yymaxutok ? "" : ",")
|
||||
|
||||
if display_name
|
||||
str << sprintf(" %-30s /* %s */\n", s, display_name)
|
||||
sprintf(" %-30s /* %s */\n", s, display_name)
|
||||
else
|
||||
str << sprintf(" %s\n", s)
|
||||
sprintf(" %s\n", s)
|
||||
end
|
||||
end
|
||||
|
||||
str
|
||||
end.join
|
||||
end
|
||||
|
||||
# b4_symbol_enum
|
||||
def symbol_enum
|
||||
str = ""
|
||||
|
||||
last_sym_number = @context.yysymbol_kind_t.last[1]
|
||||
@context.yysymbol_kind_t.each do |s_value, sym_number, display_name|
|
||||
@context.yysymbol_kind_t.map do |s_value, sym_number, display_name|
|
||||
s = sprintf("%s = %d%s", s_value, sym_number, (sym_number == last_sym_number) ? "" : ",")
|
||||
|
||||
if display_name
|
||||
str << sprintf(" %-40s /* %s */\n", s, display_name)
|
||||
sprintf(" %-40s /* %s */\n", s, display_name)
|
||||
else
|
||||
str << sprintf(" %s\n", s)
|
||||
sprintf(" %s\n", s)
|
||||
end
|
||||
end
|
||||
|
||||
str
|
||||
end.join
|
||||
end
|
||||
|
||||
def yytranslate
|
||||
@@ -132,12 +126,10 @@ module Lrama
|
||||
end
|
||||
|
||||
def symbol_actions_for_printer
|
||||
str = ""
|
||||
|
||||
@grammar.symbols.each do |sym|
|
||||
@grammar.symbols.map do |sym|
|
||||
next unless sym.printer
|
||||
|
||||
str << <<-STR
|
||||
<<-STR
|
||||
case #{sym.enum_name}: /* #{sym.comment} */
|
||||
#line #{sym.printer.lineno} "#{@grammar_file_path}"
|
||||
{#{sym.printer.translated_code(sym.tag)}}
|
||||
@@ -145,18 +137,14 @@ module Lrama
|
||||
break;
|
||||
|
||||
STR
|
||||
end
|
||||
|
||||
str
|
||||
end.join
|
||||
end
|
||||
|
||||
def symbol_actions_for_destructor
|
||||
str = ""
|
||||
|
||||
@grammar.symbols.each do |sym|
|
||||
@grammar.symbols.map do |sym|
|
||||
next unless sym.destructor
|
||||
|
||||
str << <<-STR
|
||||
<<-STR
|
||||
case #{sym.enum_name}: /* #{sym.comment} */
|
||||
#line #{sym.destructor.lineno} "#{@grammar_file_path}"
|
||||
{#{sym.destructor.translated_code(sym.tag)}}
|
||||
@@ -164,9 +152,7 @@ module Lrama
|
||||
break;
|
||||
|
||||
STR
|
||||
end
|
||||
|
||||
str
|
||||
end.join
|
||||
end
|
||||
|
||||
# b4_user_initial_action
|
||||
@@ -236,12 +222,10 @@ module Lrama
|
||||
end
|
||||
|
||||
def symbol_actions_for_error_token
|
||||
str = ""
|
||||
|
||||
@grammar.symbols.each do |sym|
|
||||
@grammar.symbols.map do |sym|
|
||||
next unless sym.error_token
|
||||
|
||||
str << <<-STR
|
||||
<<-STR
|
||||
case #{sym.enum_name}: /* #{sym.comment} */
|
||||
#line #{sym.error_token.lineno} "#{@grammar_file_path}"
|
||||
{#{sym.error_token.translated_code(sym.tag)}}
|
||||
@@ -249,22 +233,18 @@ module Lrama
|
||||
break;
|
||||
|
||||
STR
|
||||
end
|
||||
|
||||
str
|
||||
end.join
|
||||
end
|
||||
|
||||
# b4_user_actions
|
||||
def user_actions
|
||||
str = ""
|
||||
|
||||
@context.states.rules.each do |rule|
|
||||
action = @context.states.rules.map do |rule|
|
||||
next unless rule.token_code
|
||||
|
||||
code = rule.token_code
|
||||
spaces = " " * (code.column - 1)
|
||||
|
||||
str << <<-STR
|
||||
<<-STR
|
||||
case #{rule.id + 1}: /* #{rule.as_comment} */
|
||||
#line #{code.line} "#{@grammar_file_path}"
|
||||
#{spaces}{#{rule.translated_code}}
|
||||
@@ -272,14 +252,12 @@ module Lrama
|
||||
break;
|
||||
|
||||
STR
|
||||
end
|
||||
end.join
|
||||
|
||||
str << <<-STR
|
||||
action + <<-STR
|
||||
|
||||
#line [@oline@] [@ofile@]
|
||||
STR
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
def omit_blanks(param)
|
||||
@@ -343,7 +321,7 @@ module Lrama
|
||||
|
||||
# b4_parse_param_use
|
||||
def parse_param_use(val, loc)
|
||||
str = <<-STR
|
||||
str = <<-STR.dup
|
||||
YY_USE (#{val});
|
||||
YY_USE (#{loc});
|
||||
STR
|
||||
@@ -357,7 +335,8 @@ module Lrama
|
||||
|
||||
# b4_yylex_formals
|
||||
def yylex_formals
|
||||
ary = ["&yylval", "&yylloc"]
|
||||
ary = ["&yylval"]
|
||||
ary << "&yylloc" if @grammar.locations
|
||||
|
||||
if @grammar.lex_param
|
||||
ary << lex_param_name
|
||||
@@ -397,17 +376,9 @@ module Lrama
|
||||
def int_array_to_string(ary)
|
||||
last = ary.count - 1
|
||||
|
||||
s = ary.each_with_index.each_slice(10).map do |slice|
|
||||
str = " "
|
||||
|
||||
slice.each do |e, i|
|
||||
str << sprintf("%6d%s", e, (i == last) ? "" : ",")
|
||||
end
|
||||
|
||||
str
|
||||
end
|
||||
|
||||
s.join("\n")
|
||||
ary.each_with_index.each_slice(10).map do |slice|
|
||||
" " + slice.map { |e, i| sprintf("%6d%s", e, (i == last) ? "" : ",") }.join
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
def spec_mapped_header_file
|
||||
@@ -457,26 +428,24 @@ module Lrama
|
||||
end
|
||||
|
||||
def template_dir
|
||||
File.expand_path("../../../template", __FILE__)
|
||||
File.expand_path('../../template', __dir__)
|
||||
end
|
||||
|
||||
def string_array_to_string(ary)
|
||||
str = ""
|
||||
result = ""
|
||||
tmp = " "
|
||||
|
||||
ary.each do |s|
|
||||
s = s.gsub('\\', '\\\\\\\\')
|
||||
s = s.gsub('"', '\\"')
|
||||
|
||||
if (tmp + s + " \"\",").length > 75
|
||||
str << tmp << "\n"
|
||||
tmp = " \"#{s}\","
|
||||
replaced = s.gsub('\\', '\\\\\\\\').gsub('"', '\\"')
|
||||
if (tmp + replaced + " \"\",").length > 75
|
||||
result = "#{result}#{tmp}\n"
|
||||
tmp = " \"#{replaced}\","
|
||||
else
|
||||
tmp << " \"#{s}\","
|
||||
tmp = "#{tmp} \"#{replaced}\","
|
||||
end
|
||||
end
|
||||
|
||||
str << tmp
|
||||
result + tmp
|
||||
end
|
||||
|
||||
def replace_special_variables(str, ofile)
|
||||
|
||||
+750
-840
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,4 @@
|
||||
require 'lrama/report/duration'
|
||||
require 'lrama/report/profile'
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'report/duration'
|
||||
require_relative 'report/profile'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Report
|
||||
module Duration
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class Report
|
||||
module Profile
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
require "lrama/state/reduce"
|
||||
require "lrama/state/reduce_reduce_conflict"
|
||||
require "lrama/state/resolved_conflict"
|
||||
require "lrama/state/shift"
|
||||
require "lrama/state/shift_reduce_conflict"
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "state/reduce"
|
||||
require_relative "state/reduce_reduce_conflict"
|
||||
require_relative "state/resolved_conflict"
|
||||
require_relative "state/shift"
|
||||
require_relative "state/shift_reduce_conflict"
|
||||
|
||||
module Lrama
|
||||
class State
|
||||
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
|
||||
:default_reduction_rule, :closure, :items
|
||||
attr_accessor :shifts, :reduces
|
||||
attr_accessor :shifts, :reduces, :ielr_isocores, :lalr_isocore
|
||||
|
||||
def initialize(id, accessing_symbol, kernels)
|
||||
@id = id
|
||||
@@ -21,6 +23,12 @@ module Lrama
|
||||
@conflicts = []
|
||||
@resolved_conflicts = []
|
||||
@default_reduction_rule = nil
|
||||
@predecessors = []
|
||||
@lalr_isocore = self
|
||||
@ielr_isocores = [self]
|
||||
@internal_dependencies = {}
|
||||
@successor_dependencies = {}
|
||||
@always_follows = {}
|
||||
end
|
||||
|
||||
def closure=(closure)
|
||||
@@ -82,6 +90,18 @@ module Lrama
|
||||
@transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] }
|
||||
end
|
||||
|
||||
def update_transition(shift, next_state)
|
||||
set_items_to_state(shift.next_items, next_state)
|
||||
next_state.append_predecessor(self)
|
||||
clear_transitions_cache
|
||||
end
|
||||
|
||||
def clear_transitions_cache
|
||||
@nterm_transitions = nil
|
||||
@term_transitions = nil
|
||||
@transitions = nil
|
||||
end
|
||||
|
||||
def selected_term_transitions
|
||||
term_transitions.reject do |shift, next_state|
|
||||
shift.not_selected
|
||||
@@ -140,5 +160,274 @@ module Lrama
|
||||
conflict.type == :reduce_reduce
|
||||
end
|
||||
end
|
||||
|
||||
def propagate_lookaheads(next_state)
|
||||
next_state.kernels.map {|item|
|
||||
lookahead_sets =
|
||||
if item.position == 1
|
||||
goto_follow_set(item.lhs)
|
||||
else
|
||||
kernel = kernels.find {|k| k.predecessor_item_of?(item) }
|
||||
item_lookahead_set[kernel]
|
||||
end
|
||||
|
||||
[item, lookahead_sets & next_state.lookahead_set_filters[item]]
|
||||
}.to_h
|
||||
end
|
||||
|
||||
def lookaheads_recomputed
|
||||
!@item_lookahead_set.nil?
|
||||
end
|
||||
|
||||
def compatible_lookahead?(filtered_lookahead)
|
||||
!lookaheads_recomputed ||
|
||||
@lalr_isocore.annotation_list.all? {|token, actions|
|
||||
a = dominant_contribution(token, actions, item_lookahead_set)
|
||||
b = dominant_contribution(token, actions, filtered_lookahead)
|
||||
a.nil? || b.nil? || a == b
|
||||
}
|
||||
end
|
||||
|
||||
def lookahead_set_filters
|
||||
kernels.map {|kernel|
|
||||
[kernel,
|
||||
@lalr_isocore.annotation_list.select {|token, actions|
|
||||
token.term? && actions.any? {|action, contributions|
|
||||
!contributions.nil? && contributions.key?(kernel) && contributions[kernel]
|
||||
}
|
||||
}.map {|token, _| token }
|
||||
]
|
||||
}.to_h
|
||||
end
|
||||
|
||||
def dominant_contribution(token, actions, lookaheads)
|
||||
a = actions.select {|action, contributions|
|
||||
contributions.nil? || contributions.any? {|item, contributed| contributed && lookaheads[item].include?(token) }
|
||||
}.map {|action, _| action }
|
||||
return nil if a.empty?
|
||||
a.reject {|action|
|
||||
if action.is_a?(State::Shift)
|
||||
action.not_selected
|
||||
elsif action.is_a?(State::Reduce)
|
||||
action.not_selected_symbols.include?(token)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def inadequacy_list
|
||||
return @inadequacy_list if @inadequacy_list
|
||||
|
||||
shift_contributions = shifts.map {|shift|
|
||||
[shift.next_sym, [shift]]
|
||||
}.to_h
|
||||
reduce_contributions = reduces.map {|reduce|
|
||||
(reduce.look_ahead || []).map {|sym|
|
||||
[sym, [reduce]]
|
||||
}.to_h
|
||||
}.reduce(Hash.new([])) {|hash, cont|
|
||||
hash.merge(cont) {|_, a, b| a | b }
|
||||
}
|
||||
|
||||
list = shift_contributions.merge(reduce_contributions) {|_, a, b| a | b }
|
||||
@inadequacy_list = list.select {|token, actions| token.term? && actions.size > 1 }
|
||||
end
|
||||
|
||||
def annotation_list
|
||||
return @annotation_list if @annotation_list
|
||||
|
||||
@annotation_list = annotate_manifestation
|
||||
@annotation_list = @items_to_state.values.map {|next_state| next_state.annotate_predecessor(self) }
|
||||
.reduce(@annotation_list) {|result, annotations|
|
||||
result.merge(annotations) {|_, actions_a, actions_b|
|
||||
if actions_a.nil? || actions_b.nil?
|
||||
actions_a || actions_b
|
||||
else
|
||||
actions_a.merge(actions_b) {|_, contributions_a, contributions_b|
|
||||
if contributions_a.nil? || contributions_b.nil?
|
||||
next contributions_a || contributions_b
|
||||
end
|
||||
|
||||
contributions_a.merge(contributions_b) {|_, contributed_a, contributed_b|
|
||||
contributed_a || contributed_b
|
||||
}
|
||||
}
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def annotate_manifestation
|
||||
inadequacy_list.transform_values {|actions|
|
||||
actions.map {|action|
|
||||
if action.is_a?(Shift)
|
||||
[action, nil]
|
||||
elsif action.is_a?(Reduce)
|
||||
if action.rule.empty_rule?
|
||||
[action, lhs_contributions(action.rule.lhs, inadequacy_list.key(actions))]
|
||||
else
|
||||
contributions = kernels.map {|kernel| [kernel, kernel.rule == action.rule && kernel.end_of_rule?] }.to_h
|
||||
[action, contributions]
|
||||
end
|
||||
end
|
||||
}.to_h
|
||||
}
|
||||
end
|
||||
|
||||
def annotate_predecessor(predecessor)
|
||||
annotation_list.transform_values {|actions|
|
||||
token = annotation_list.key(actions)
|
||||
actions.transform_values {|inadequacy|
|
||||
next nil if inadequacy.nil?
|
||||
lhs_adequacy = kernels.any? {|kernel|
|
||||
inadequacy[kernel] && kernel.position == 1 && predecessor.lhs_contributions(kernel.lhs, token).nil?
|
||||
}
|
||||
if lhs_adequacy
|
||||
next nil
|
||||
else
|
||||
predecessor.kernels.map {|pred_k|
|
||||
[pred_k, kernels.any? {|k|
|
||||
inadequacy[k] && (
|
||||
pred_k.predecessor_item_of?(k) && predecessor.item_lookahead_set[pred_k].include?(token) ||
|
||||
k.position == 1 && predecessor.lhs_contributions(k.lhs, token)[pred_k]
|
||||
)
|
||||
}]
|
||||
}.to_h
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def lhs_contributions(sym, token)
|
||||
shift, next_state = nterm_transitions.find {|sh, _| sh.next_sym == sym }
|
||||
if always_follows(shift, next_state).include?(token)
|
||||
nil
|
||||
else
|
||||
kernels.map {|kernel| [kernel, follow_kernel_items(shift, next_state, kernel) && item_lookahead_set[kernel].include?(token)] }.to_h
|
||||
end
|
||||
end
|
||||
|
||||
def follow_kernel_items(shift, next_state, kernel)
|
||||
queue = [[self, shift, next_state]]
|
||||
until queue.empty?
|
||||
st, sh, next_st = queue.pop
|
||||
return true if kernel.next_sym == sh.next_sym && kernel.symbols_after_transition.all?(&:nullable)
|
||||
st.internal_dependencies(sh, next_st).each {|v| queue << v }
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def item_lookahead_set
|
||||
return @item_lookahead_set if @item_lookahead_set
|
||||
|
||||
kernels.map {|item|
|
||||
value =
|
||||
if item.lhs.accept_symbol?
|
||||
[]
|
||||
elsif item.position > 1
|
||||
prev_items = predecessors_with_item(item)
|
||||
prev_items.map {|st, i| st.item_lookahead_set[i] }.reduce([]) {|acc, syms| acc |= syms }
|
||||
elsif item.position == 1
|
||||
prev_state = @predecessors.find {|p| p.shifts.any? {|shift| shift.next_sym == item.lhs } }
|
||||
shift, next_state = prev_state.nterm_transitions.find {|shift, _| shift.next_sym == item.lhs }
|
||||
prev_state.goto_follows(shift, next_state)
|
||||
end
|
||||
[item, value]
|
||||
}.to_h
|
||||
end
|
||||
|
||||
def item_lookahead_set=(k)
|
||||
@item_lookahead_set = k
|
||||
end
|
||||
|
||||
def predecessors_with_item(item)
|
||||
result = []
|
||||
@predecessors.each do |pre|
|
||||
pre.items.each do |i|
|
||||
result << [pre, i] if i.predecessor_item_of?(item)
|
||||
end
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
def append_predecessor(prev_state)
|
||||
@predecessors << prev_state
|
||||
@predecessors.uniq!
|
||||
end
|
||||
|
||||
def goto_follow_set(nterm_token)
|
||||
return [] if nterm_token.accept_symbol?
|
||||
shift, next_state = @lalr_isocore.nterm_transitions.find {|sh, _| sh.next_sym == nterm_token }
|
||||
|
||||
@kernels
|
||||
.select {|kernel| follow_kernel_items(shift, next_state, kernel) }
|
||||
.map {|kernel| item_lookahead_set[kernel] }
|
||||
.reduce(always_follows(shift, next_state)) {|result, terms| result |= terms }
|
||||
end
|
||||
|
||||
def goto_follows(shift, next_state)
|
||||
queue = internal_dependencies(shift, next_state) + predecessor_dependencies(shift, next_state)
|
||||
terms = always_follows(shift, next_state)
|
||||
until queue.empty?
|
||||
st, sh, next_st = queue.pop
|
||||
terms |= st.always_follows(sh, next_st)
|
||||
st.internal_dependencies(sh, next_st).each {|v| queue << v }
|
||||
st.predecessor_dependencies(sh, next_st).each {|v| queue << v }
|
||||
end
|
||||
terms
|
||||
end
|
||||
|
||||
def always_follows(shift, next_state)
|
||||
return @always_follows[[shift, next_state]] if @always_follows[[shift, next_state]]
|
||||
|
||||
queue = internal_dependencies(shift, next_state) + successor_dependencies(shift, next_state)
|
||||
terms = []
|
||||
until queue.empty?
|
||||
st, sh, next_st = queue.pop
|
||||
terms |= next_st.term_transitions.map {|sh, _| sh.next_sym }
|
||||
st.internal_dependencies(sh, next_st).each {|v| queue << v }
|
||||
st.successor_dependencies(sh, next_st).each {|v| queue << v }
|
||||
end
|
||||
@always_follows[[shift, next_state]] = terms
|
||||
end
|
||||
|
||||
def internal_dependencies(shift, next_state)
|
||||
return @internal_dependencies[[shift, next_state]] if @internal_dependencies[[shift, next_state]]
|
||||
|
||||
syms = @items.select {|i|
|
||||
i.next_sym == shift.next_sym && i.symbols_after_transition.all?(&:nullable) && i.position == 0
|
||||
}.map(&:lhs).uniq
|
||||
@internal_dependencies[[shift, next_state]] = nterm_transitions.select {|sh, _| syms.include?(sh.next_sym) }.map {|goto| [self, *goto] }
|
||||
end
|
||||
|
||||
def successor_dependencies(shift, next_state)
|
||||
return @successor_dependencies[[shift, next_state]] if @successor_dependencies[[shift, next_state]]
|
||||
|
||||
@successor_dependencies[[shift, next_state]] =
|
||||
next_state.nterm_transitions
|
||||
.select {|next_shift, _| next_shift.next_sym.nullable }
|
||||
.map {|transition| [next_state, *transition] }
|
||||
end
|
||||
|
||||
def predecessor_dependencies(shift, next_state)
|
||||
state_items = []
|
||||
@kernels.select {|kernel|
|
||||
kernel.next_sym == shift.next_sym && kernel.symbols_after_transition.all?(&:nullable)
|
||||
}.each do |item|
|
||||
queue = predecessors_with_item(item)
|
||||
until queue.empty?
|
||||
st, i = queue.pop
|
||||
if i.position == 0
|
||||
state_items << [st, i]
|
||||
else
|
||||
st.predecessors_with_item(i).each {|v| queue << v }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
state_items.map {|state, item|
|
||||
sh, next_st = state.nterm_transitions.find {|shi, _| shi.next_sym == item.lhs }
|
||||
[state, sh, next_st]
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class State
|
||||
class Reduce
|
||||
@@ -24,8 +26,8 @@ module Lrama
|
||||
end
|
||||
|
||||
def selected_look_ahead
|
||||
if @look_ahead
|
||||
@look_ahead - @not_selected_symbols
|
||||
if look_ahead
|
||||
look_ahead - @not_selected_symbols
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Lrama
|
||||
class State
|
||||
class ReduceReduceConflict < Struct.new(:symbols, :reduce1, :reduce2, keyword_init: true)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user