Skip to content

Commit

Permalink
Convert to using integer language revisions internally
Browse files Browse the repository at this point in the history
Use 1+ revision numbers for language revisions where 1 stands for 6.c.
I know this is a mega-commit, but due to the scale of the change it
cannot be split into smaller parts as nearly anywhere we rely on the
versioning there is no way to smoothly transition from legacy to the new
one.

The primary reason for the change is to simplify transitioning from
Perl6-styled `v6.c` scheme to something new, like `v2015`, perhaps, or
whatever would be the accepted solution in the problem-solving. The
point is to separate the internal representation from the public one.

Another advantage is simplification of version checks as the previsous
`.lang-rev-before` method call can now be replaced with faster numeric
comparisons.

Notable changes:

- CORE-SETTING-REV are now `Int`s with a helper `LanguageRevision` role
  mixin. The role is an implementation details, thus not published into
  the user space. The role acts somewhat like allomorphs except that it
  is revision-centric and may provide some extra related APIs in the
  future.

  Role name similarity to `Perl6::Metamodel::LanguageRevision` is rather
  accidental and I'd like it to remain self-explaining for when
  `CORE-SETTING-REV` is being introspected.

- `Perl6::Metamodel::LanguageRevision` role has slightly changed
  semantics of its `language-revision` method which is considered a
  public API and is specced. The method now returns same type as
  `CORE-SETTING-REV`: `Int` with the mixin role.

- For faster operations `Perl6::Metamodel::LanguageRevision` got a new
  `language_revision` method which returns just a plain `int`, same as
  `Perl6::Compiler` method of the same name does.

- `Perl6::Compiler` got a new method `lvs` (Language Version Services).
  The methods returns a class which implements basic language
  version/revision manipulations like converting from public
  reprenstation into internal and vice versa.

- Another new method on the compiler is `language_version_parts`. It
  returns list of parts of the version internal representation. I.e. for
  public `v6.e.PREVIEW` it would return `(3, 'PREVIEW')`.

- Changed values in the compiler configuarion, but these are
  implementation details and should do not require detailed
  explanatation.
  • Loading branch information
vrurg committed Jan 15, 2023
1 parent 424de2f commit e1a7ca6
Show file tree
Hide file tree
Showing 34 changed files with 416 additions and 261 deletions.
18 changes: 9 additions & 9 deletions src/Perl6/Actions.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ my int $?BITS := nqp::isgt_i(nqp::add_i(2147483648, 1), 0) ?? 64 !! 32;
sub block_closure($code, :$regex) {
my $clone := QAST::Op.new( :op('callmethod'), :name('clone'), $code );
if $regex {
if $*W.lang-rev-before('d') {
if nqp::getcomp('Raku').language_revision < 2 {
my $marker := $*W.find_symbol(['Rakudo', 'Internals', 'RegexBoolification6cMarker']);
$clone.push(QAST::WVal.new( :value($marker), :named('topic') ));
}
Expand Down Expand Up @@ -1450,7 +1450,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
QAST::WVal.new(:value($main<value>)),
$mainline # run the mainline and get its result
);
unless $world.lang-rev-before('d') {
unless nqp::getcomp('Raku').language_revision < 2 {
$mainline.push(
QAST::WVal.new( # $*IN as $*ARGSFILES
value => $world.find_symbol(['Bool','True'], :setting-only),
Expand Down Expand Up @@ -1647,7 +1647,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
my $ast := $statements[0].ast;

# an op and 6e or higher?
if nqp::istype($ast,QAST::Op) && !$*W.lang-rev-before("e") {
if nqp::istype($ast,QAST::Op) && nqp::getcomp('Raku').language_revision >= 3 {
sub is-pipe-pipe($ast) {
nqp::istype($ast,QAST::Op)
&& $ast.name eq '&prefix:<|>'
Expand Down Expand Up @@ -2582,7 +2582,7 @@ class Perl6::Actions is HLL::Actions does STDActions {

sub single_top_level_whenever($block) {
if $*WHENEVER_COUNT == 1
&& nqp::getcomp('Raku').language_version ne '6.c' {
&& nqp::getcomp('Raku').language_revision > 1 {
my $stmts := $block[1];
if nqp::istype($stmts, QAST::Stmts) {
my @stmts := $stmts.list;
Expand Down Expand Up @@ -2667,7 +2667,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
QAST::WVal.new( :value($world.find_single_symbol_in_setting('Promise')) ),
$<blorst>.ast
);
unless $world.lang-rev-before('d') {
if nqp::getcomp('Raku').language_revision > 1 {
$qast.push(QAST::WVal.new(
:value($world.find_symbol(['Bool', 'True'])),
:named('report-broken-if-sunk')
Expand Down Expand Up @@ -3027,7 +3027,7 @@ class Perl6::Actions is HLL::Actions does STDActions {

method contextualizer($/) {
my $past := $<coercee>.ast;
my $has_magic := $*W.lang-rev-before('d') && $<coercee> eq '';
my $has_magic := nqp::getcomp('Raku').language_revision < 2 && $<coercee> eq '';
my $sigil := ~$<sigil>;

if $has_magic && $sigil eq '$' { # for '$()'
Expand Down Expand Up @@ -4125,7 +4125,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
method routine_declarator:sym<submethod>($/) { make $<method_def>.ast; }

sub decontrv_op() {
$*W.lang-rev-before('d') && nqp::getcomp('Raku').backend.name eq 'moar'
(my $comp := nqp::getcomp('Raku')).language_revision < 2 && $comp.backend.name eq 'moar'
?? 'p6decontrv_6c'
!! 'p6decontrv'
}
Expand Down Expand Up @@ -5458,7 +5458,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
}
if $sigil eq '%' {
nqp::defined($*OFTYPE) && $world.throw: $/, 'X::ParametricConstant';
$world.lang-rev-before('d')
nqp::getcomp('Raku').language_revision < 2
?? check-type($world.find_symbol: ['Associative'])
!! check-type-maybe-coerce('Map', $world.find_symbol: ['Associative'])
}
Expand Down Expand Up @@ -6852,7 +6852,6 @@ class Perl6::Actions is HLL::Actions does STDActions {
}

method arglist($/) {
my $Pair := $*W.find_single_symbol_in_setting('Pair');
my $past := QAST::Op.new( :op('call'), :node($/) );
my @names;
if $<EXPR> {
Expand Down Expand Up @@ -6889,6 +6888,7 @@ class Perl6::Actions is HLL::Actions does STDActions {
migrate_colonpairs($/, @args);
}
my %named_counts;
my $Pair := $*W.find_single_symbol_in_setting('Pair');
for @args {
if nqp::istype($_, QAST::Op) && istype($_.returns, $Pair)
&& nqp::can($_[1], 'has_compile_time_value') {
Expand Down
153 changes: 134 additions & 19 deletions src/Perl6/Compiler.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,125 @@ use QRegex;
use Perl6::Optimizer;

class Perl6::Compiler is HLL::Compiler {
has $!language_version; # Default language version in form 6.c
has $!language_modifier; # Active language modifier; PREVIEW mostly.
has $!language_revisions; # Hash of language revision letters. See gen/<vm>/main-version.nqp
has $!can_language_versions; # List of valid language version
has @!language_version; # Default language revision, 1 stands for v6.c
has $!language_revisions; # Hash of language revision properties. See gen/<vm>/main-version.nqp
has $!can_language_versions; # List of valid language versions
has $!rakudo-home;

class LanguageVersionServices {
method p6rev(int $internal-revision) {
$internal-revision < 1
?? nqp::die("Internal revision " ~ $internal-revision ~ " cannot be converted into Perl6 representation")
!! nqp::chr(98 + $internal-revision)
}

method internal-from-p6(str $p6rev) {
nqp::chars($p6rev) > 1
?? nqp::die("Perl6 revision can only be a single letter, got '$p6rev'")
!! (my $irev := nqp::ord($p6rev) - 98) < 1
?? nqp::die("'$p6rev' cannot be a Perl6 revision")
!! $irev
}

my sub as(@parts, :$plus, :$as-str, :$as-version) {
if $as-str || $as-version {
my $i := -1;
while ++$i < +@parts {
@parts[$i] := ~@parts[$i];
}

my $vstr := join(".", @parts) ~ $plus;

return $vstr unless $as-version;

my $Version := nqp::gethllsym('Raku', 'Version');
if nqp::isnull($Version) {
nqp::die("Symbol 'Version' is not available at this time; is BOOTSTRAP loaded?")
}

# This can be micro-optimized by using nqp::create + nqp::bindattr, but does it make any sense?
return $Version.new($vstr);
}
@parts
}

# Get language version representation, guess its format, and return it as internal. with :as-version
# will try to get Version class from HLL symbols and return an instance of it.
method from-public-repr(str $repr, :$as-str, :$as-version) {
my $config := nqp::gethllsym('default', 'SysConfig');
my @parts;
my $plus := '';

if !$repr || $repr eq 'v6' {
# Default revision implied
@parts.push: nqp::gethllsym('default', 'SysConfig').rakudo-build-config<language-revision>;
}
else {
my $m := $repr ~~ /^ v? $<vstr>=[ [ \w+ | '*' ]+ % '.' ] $<plus>='+'? $/;
@parts := nqp::split('.', $m<vstr>);
$plus := ~$m<plus>;
if $repr ~~ /^ v? 6 [\D | $]/ {
# Perl6 representation. Skip the starting '6' first
if @parts[0] eq '6' {
# Since we excluded the 'v6' variant then @parts would never end up being empty here
@parts.shift;
}
else {
@parts[0] := nqp::substr(@parts[0], 1);
}

if @parts[0] ne '*' {
@parts[0] := self.internal-from-p6(@parts[0]);
}
}
else {
nqp::die("Requested language version '$repr' is not valid");
}
}

as(@parts, :$plus, :$as-str, :$as-version)
}

method as-perl6($internal, :$as-str, :$as-version) {
my $Version := nqp::gethllsym('Raku', 'Version');
my $primspec := nqp::objprimspec($internal);
my $is-list := nqp::islist($internal);
my $is-version := !nqp::isnull($Version) && nqp::istype($internal, $Version);
my @parts;
if $primspec == 3 || $is-list || $is-version {
# A string
@parts := $is-version
?? nqp::clone(nqp::getattr($internal, $Version, '$!parts'))
!! $is-list
?? nqp::clone($internal)
!! nqp::split(".", $internal);
my $rev := @parts[0];
if nqp::objprimspec($rev) == 3 && $rev ~~ /^ v? $<vnum>=\d+ $<plus>='+'? $/ -> $m {
# Turn internal revision number info a Perl6-revision char
@parts[0] := self.p6rev(+$m<vnum>) ~ $m<plus>;
}
else {
@parts[0] := self.p6rev($rev);
}
}
elsif $primspec == 1 || $primspec == 10 { # int, unit
@parts.push: self.p6rev($internal);
}
else {
nqp::die("Don't know how to create Perl6-style language version from " ~ $internal.HOW.name($internal));
}

@parts.unshift('6');

as(@parts, :$as-str, :$as-version)
}

method as-public-repr($internal, :$as-str, :$as-version) {
# When v6.x is replaced with another representation this method will change too.
self.as-perl6($internal, :$as-str, :$as-version);
}
}

method config() {
nqp::gethllsym('default', 'SysConfig').rakudo-build-config();
}
Expand All @@ -18,6 +131,8 @@ class Perl6::Compiler is HLL::Compiler {
nqp::exit(0);
}

method lvs() { LanguageVersionServices }

method version_string(:$shorten-versions, :$no-unicode) {
my $config-version := self.config()<version>;
my $backend-version := nqp::getattr(self,HLL::Compiler,'$!backend').version_string;
Expand Down Expand Up @@ -60,28 +175,28 @@ class Perl6::Compiler is HLL::Compiler {
method implementation() { self.config<implementation> }
method language_name() { 'Raku' }
method reset_language_version() {
$!language_version := NQPMu;
$!language_modifier := NQPMu;
@!language_version := [];
}
method set_language_version($version) {
$!language_version := $version;
@!language_version := nqp::islist($version)
?? $version
!! self.lvs.from-public-repr($version);
}
method set_language_modifier($modifier) {
$!language_modifier := $modifier;
method set_language_revision(int $rev) {
@!language_version := [$rev];
}
method language_version() {
if nqp::defined($!language_version) {
$!language_version
}
else {
$!language_version := %*COMPILING<%?OPTIONS><language_version> || self.config<language-version>
method language_version_parts() {
unless nqp::defined(@!language_version) && @!language_version {
@!language_version := self.lvs.from-public-repr(%*COMPILING<%?OPTIONS><language_version> // '');
}
@!language_version
}
method language_revision() {
nqp::substr(self.language_version,2,1)
method language_version() {
self.set_language_version('') unless @!language_version;
LanguageVersionServices.as-public-repr: @!language_version, :as-str
}
method language_modifier() {
$!language_modifier
method language_revision() {
self.language_version_parts[0]
}
method can_language_versions() {
$!can_language_versions
Expand Down
7 changes: 2 additions & 5 deletions src/Perl6/Grammar.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -1194,10 +1194,7 @@ grammar Perl6::Grammar is HLL::Grammar does STD {
rule statement_control:sym<whenever> {
<sym><.kok>
[
|| <?{
nqp::getcomp('Raku').language_version eq '6.c'
|| $*WHENEVER_COUNT >= 0
}>
|| <?{ nqp::getcomp('Raku').language_revision == 1 || $*WHENEVER_COUNT >= 0 }>
|| <.typed_panic('X::Comp::WheneverOutOfScope')>
]
{ $*WHENEVER_COUNT++ }
Expand Down Expand Up @@ -4621,7 +4618,7 @@ grammar Perl6::Grammar is HLL::Grammar does STD {
self.typed_panic(
'X::Syntax::Extension::Category', :$category
) if nqp::iseq_s($subname, "$category:<$opname>")
|| nqp::iseq_s($subname, "$category:sym<$opname>") && $*W.lang-rev-before('d');
|| nqp::iseq_s($subname, "$category:sym<$opname>") && nqp::getcomp('Raku').language_revision < 2;
self.typed_panic(
'X::Syntax::Reserved', :reserved(':sym<> colonpair')
Expand Down
9 changes: 7 additions & 2 deletions src/Perl6/Metamodel/BUILDPLAN.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ role Perl6::Metamodel::BUILDPLAN {
my @attrs := $obj.HOW.attributes($obj, :local(1));
# When adding role's BUILD/TWEAK into the buildplan for pre-6.e classes only roles of 6.e+ origin must be
# considered.
my $only_6e_roles := $obj.HOW.lang-rev-before($obj, 'e');
my $ohow := $obj.HOW;
my $only_6e_roles := nqp::can($ohow, 'language_revision')
?? $ohow.language_revision($obj) < 3
!! nqp::can($ohow, 'lang-rev-before')
?? $ohow.lang-rev-before($obj, 'e') # Support legacy approach where implemented
!! 1; # Assume the HOW being compiled against an older Raku language version

# Emit any container initializers. Also build hash of attrs we
# do not touch in any of the BUILDPLAN so we can spit out vivify
Expand Down Expand Up @@ -90,7 +95,7 @@ role Perl6::Metamodel::BUILDPLAN {
while --$i >= 0 {
my $role := @ins_roles[$i];
# Skip any non-6.e+ role if the target is pre-6.e
next if $only_6e_roles && $role.HOW.lang-rev-before($role, 'e');
next if $only_6e_roles && $role.HOW.language_revision($role) < 3;
my $submeth := nqp::atkey($role.HOW.submethod_table($role), $name);
if !nqp::isnull($submeth) {
nqp::push(@plan, $submeth);
Expand Down
4 changes: 2 additions & 2 deletions src/Perl6/Metamodel/ClassHOW.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ class Perl6::Metamodel::ClassHOW
# If class is a result of pun then transfer hidden flag from the source role
if $!pun_source =:= $r {
self.set_hidden($obj) if $ins.HOW.hidden($ins);
self.set_language_revision($obj, $ins.HOW.language-revision($ins), :force);
self.set_language_revision($obj, $ins.HOW.language_revision($ins), :force);
}
@ins_roles.push($ins);
self.add_concretization($obj, $r, $ins);
Expand Down Expand Up @@ -313,7 +313,7 @@ class Perl6::Metamodel::ClassHOW
my $i := 0;
while ++$i < +@mro {
my $parent := @mro[$i];
if nqp::can($parent.HOW, 'find_method_fallback')
if nqp::can($parent.HOW, 'find_method_fallback')
&& !nqp::isnull(my $fallback := $parent.HOW.find_method_fallback($obj, $name, :local)) {
return $fallback
}
Expand Down
4 changes: 4 additions & 0 deletions src/Perl6/Metamodel/Configuration.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ class Perl6::Metamodel::Configuration {
$utility_class.NEXT-ID
}

my $language-revision-role := nqp::null();
method set_language_revision_role($role) { $language-revision-role := $role; }
method language_revision_role() { $language-revision-role }

# Register HLL symbol for code which doesn't have direct access to this class. For example, moar/Perl6/Ops.nqp
# relies on this symbol.
nqp::bindhllsym('Raku', 'METAMODEL_CONFIGURATION', Perl6::Metamodel::Configuration);
Expand Down
2 changes: 1 addition & 1 deletion src/Perl6/Metamodel/CurriedRoleHOW.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class Perl6::Metamodel::CurriedRoleHOW
$!candidate := $!curried_role.HOW.select_candidate($!curried_role, @pos_args, %!named_args);
my $candidate-how := $!candidate.HOW;

self.set_language_revision($obj, $candidate-how.language-revision($!candidate));
self.set_language_revision($obj, $candidate-how.language_revision($!candidate));

my $type_env;
try {
Expand Down
2 changes: 1 addition & 1 deletion src/Perl6/Metamodel/EnumHOW.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class Perl6::Metamodel::EnumHOW
my $r := @roles_to_compose.pop();
@!role_typecheck_list[+@!role_typecheck_list] := $r;
my $ins := $r.HOW.specialize($r, $obj);
self.check-type-compat($obj, $ins, ['e'])
self.check-type-compat($obj, $ins, [3])
if nqp::istype($ins.HOW, Perl6::Metamodel::LanguageRevision);
@ins_roles.push($ins);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Perl6/Metamodel/Finalization.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ role Perl6::Metamodel::Finalization {
if !nqp::isnull($destroy) && $destroy {
nqp::push(@destroyers, $destroy);
}
if !self.lang-rev-before($obj, 'e')
if self.language_revision($obj) >= 3
&& nqp::can($classHOW, 'ins_roles')
&& nqp::can($classHOW, 'roles')
{
Expand Down
Loading

0 comments on commit e1a7ca6

Please sign in to comment.