Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
- VCL: Varnish Configuration Language (`vcl`)
- Velocity (`velocity`)
- Verilog and System Verilog (`verilog`)
- Veryl (`veryl`)
- VHDL 2008 (`vhdl`)
- VimL (`viml`)
- Visual Basic (`vb`)
Expand Down
24 changes: 24 additions & 0 deletions lib/rouge/demos/veryl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// Module A
module ModuleA #(
param ParamA: u32 = 10,
) (
i_clk : input clock ,
i_rst : input reset ,
i_sel : input logic ,
i_data: input logic<ParamA> [2],
o_data: output logic<ParamA> ,
) {
var r_data: logic<ParamA>;

always_ff {
if_reset {
r_data = 0;
} else if i_sel {
r_data = i_data[0];
} else {
r_data = i_data[1];
}
}

assign o_data = r_data;
}
109 changes: 109 additions & 0 deletions lib/rouge/lexers/veryl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
module Lexers
class Veryl < RegexLexer
title "Veryl"
desc "The Veryl hardware description language (https://veryl-lang.org)"
tag 'veryl'
filenames '*.veryl'
mimetypes 'text/x-veryl'

# Characters

WHITE_SPACE = /\s+/
NEWLINE = /\n/

# Comments

LINE_COMMENT = /\/\/(?:(?!#{NEWLINE}).)*/
GENERAL_COMMENT = /\/\*(?:(?!\*\/).)*\*\//m
COMMENT = /#{LINE_COMMENT}|#{GENERAL_COMMENT}/

# Numeric literals

EXPONENT = /[0-9]+(?:_[0-9]+)*\.[0-9]+(?:_[0-9]+)*[eE][+-]?[0-9]+(?:_[0-9]+)*/
FIXED_POINT = /[0-9]+(?:_[0-9]+)*\.[0-9]+(?:_[0-9]+)*/
BASED = /(?:[0-9]+(?:_[0-9]+)*)?'s?[bodh][0-9a-fA-FxzXZ]+(?:_[0-9a-fA-FxzXZ]+)*/
ALL_BIT = /(?:[0-9]+(?:_[0-9]+)*)?'[01xzXZ]/
BASE_LESS = /[0-9]+(?:_[0-9]+)*/

# Operators and delimiters

OPERATOR = / -: | -> | \+: | \+= | -=
| \*= | \/= | %= | &= | \|=
| \^= | <<= | >>= |<<<= |>>>=
| <> | \*\* | \/ | \| | %
| \+ | - | <<< | >>> | <<
| >> | <= | >= | <: | >:
| === | ==\? | \!== | \!=\? | ==
| \!= | && | \|\| | & | \^~
| \^ | ~\^ | \| | ~& | ~\|
| \! | ~
/x

SEPARATOR = / ::< | :: | : | , | \.\.=
| \.\. | \. | = | \# | <
| \? | ' | '\{ | \{ | \[
| \( | > | \} | \] | \)
| ; | \*
/x

# Identifiers

DOLLAR_IDENTIFIER = /\$[a-zA-Z_][0-9a-zA-Z_$]*/
IDENTIFIER = /(?:r#)?[a-zA-Z_][0-9a-zA-Z_$]*/

# Keywords

def self.keywords
@keywords ||= Set.new %w(
embed enum function include interface modport module package proto pub struct union unsafe
alias always_comb always_ff assign as bind connect const final import initial inst let param return break type var
converse inout input output same
case default else if_reset if inside outside switch
for in repeat rev step
)
end

def self.keywords_type
@keywords_type ||= Set.new %w(
bit bool clock clock_posedge clock_negedge f32 f64 i8 i16 i32 i64 logic reset reset_async_high reset_async_low reset_sync_high reset_sync_low signed string tri u8 u16 u32 u64
)
end

state :root do
rule(COMMENT , Comment )
rule(EXPONENT , Num::Float )
rule(FIXED_POINT , Num::Float )
rule(BASED , Num::Integer )
rule(ALL_BIT , Num::Integer )
rule(BASE_LESS , Num::Integer )
rule(OPERATOR , Operator )
rule(SEPARATOR , Punctuation )
rule(DOLLAR_IDENTIFIER, Name )
rule(WHITE_SPACE , Text )
rule(/"/ , Str::Double, :string)

rule IDENTIFIER do |m|
name = m[0]

if self.class.keywords.include? name
token Keyword
elsif self.class.keywords_type.include? name
token Keyword::Type
else
token Name
end
end
end

state :string do
rule(/[^\\"]+/, Str::Double )
rule(/\\./ , Str::Escape )
rule(/"/ , Str::Double, :pop!)
end
end
end
end
18 changes: 18 additions & 0 deletions spec/lexers/veryl_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

describe Rouge::Lexers::Veryl do
let(:subject) { Rouge::Lexers::Veryl.new }

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.veryl'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'text/x-veryl'
end
end
end
155 changes: 155 additions & 0 deletions spec/visual/samples/veryl
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// The following code is from https://github.com/veryl-lang/doc/tree/main/book/src/04_code_examples
// Copyright (c) 2024 Naoye Hatta <[email protected]>
// Provided under Apache-2.0 or MIT license

// module definition
module ModuleA #(
param ParamA: u32 = 10,
const ParamB: u32 = 10, // trailing comma is allowed
) (
i_clk : input clock , // `clock` is a special type for clock
i_rst : input reset , // `reset` is a special type for reset
i_sel : input logic ,
i_data: input logic<ParamA> [2], // `[]` means unpacked array in SystemVerilog
o_data: output logic<ParamA> , // `<>` means packed array in SystemVerilog
) {
// const parameter declaration
// `param` is not allowed in module
const ParamC: u32 = 10;

// variable declaration
var r_data0: logic<ParamA>;
var r_data1: logic<ParamA>;
var r_data2: logic<ParamA>;

// value binding
let _w_data2: logic<ParamA> = i_data[0];

// always_ff statement with reset
// `always_ff` can take a mandatory clock and a optional reset
// `if_reset` means `if (i_rst)`. This conceals reset porality
// `()` of `if` is not required
// `=` in `always_ff` is non-blocking assignment
always_ff (i_clk, i_rst) {
if_reset {
r_data0 = 0;
} else if i_sel {
r_data0 = i_data[0];
} else {
r_data0 = i_data[1];
}
}


// always_ff statement without reset
always_ff (i_clk) {
r_data1 = r_data0;
}

// clock and reset can be omitted
// if there is a single clock and reset in the module
always_ff {
r_data2 = r_data1;
}

assign o_data = r_data1;
}

module ModuleA #(
param ParamA: u32 = 10,
) (
i_clk : input clock ,
i_rst : input reset ,
i_data: input logic<ParamA>,
o_data: output logic<ParamA>,
) {
var r_data1: logic<ParamA>;
var r_data2: logic<ParamA>;

assign r_data1 = i_data + 1;
assign o_data = r_data2 + 2;

// instance declaration
// `inst` keyword starts instance declaration
// port connnection can be specified by `()`
// each port connection is `[port_name]:[variable]`
// `[port_name]` means `[port_name]:[port_name]`
inst u_module_b: ModuleB (
i_clk ,
i_data: r_data1,
o_data: r_data2,
);

// instance declaration with parameter override
// notation of parameter connection is the same as port
inst u_module_c: ModuleC #( ParamA, ParamB: 10 );
}

module ModuleB #(
param ParamA: u32 = 10,
) (
i_clk : input clock ,
i_data: input logic<ParamA>,
o_data: output logic<ParamA>,
) {
assign o_data = 1;
}

module ModuleC #(
param ParamA: u32 = 10,
param ParamB: u32 = 10,
) () {}

// interface definition
interface InterfaceA #(
param ParamA: u32 = 1,
param ParamB: u32 = 1,
) {
const ParamC: u32 = 1;

var a: logic<ParamA>;
var b: logic<ParamA>;
var c: logic<ParamA>;

// modport definition
modport master {
a: input ,
b: input ,
c: output,
}

modport slave {
a: input ,
b: input ,
c: output,
}
}

module ModuleA (
i_clk: input clock,
i_rst: input reset,
// port declaration by modport
intf_a_mst: modport InterfaceA::master,
intf_a_slv: modport InterfaceA::slave ,
) {
// interface instantiation
inst u_intf_a: InterfaceA [10];
}

// package definition
package PackageA {
const ParamA: u32 = 1;
const ParamB: u32 = 1;

function FuncA (
a: input logic<ParamA>,
) -> logic<ParamA> {
return a + 1;

}
}

module ModuleA {
let a : logic<10> = PackageA::ParamA;
let _b: logic<10> = PackageA::FuncA(a);
}