Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a Lexer for Bicep language #1937

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
10 changes: 10 additions & 0 deletions lib/rouge/demos/bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
targetScope = 'subscription' // To create a resource group

@description('The Azure region to create the resources in.')
param location string

// Create a resource group
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: 'rg-sample'
location: location
}
110 changes: 110 additions & 0 deletions lib/rouge/lexers/bicep.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
module Rouge
module Lexers
class Bicep < Rouge::RegexLexer
tag 'bicep'
filenames '*.bicep'

title "Bicep"
desc 'Bicep is a domain-specific language (DSL) that uses declarative syntax to deploy Azure resources.'

def self.keywords
@keywords ||= Set.new %w(
resource module param var output targetScope dependsOn
existing for in if else true false null
)
end

def self.datatypes
@datatypes ||= Set.new %w(array bool int object string)
end

def self.functions
@functions ||= Set.new %w(
any array concat contains empty first intersection items last length min max range skip
take union dateTimeAdd utcNow deployment environment loadFileAsBase64 loadTextContent int
json extensionResourceId getSecret list listKeys listKeyValue listAccountSas listSecrets
pickZones reference resourceId subscriptionResourceId tenantResourceId managementGroup
resourceGroup subscription tenant base64 base64ToJson base64ToString dataUri dataUriToString
endsWith format guid indexOf lastIndexOf length newGuid padLeft replace split startsWith
string substring toLower toUpper trim uniqueString uri uriComponent uriComponentToString
toObject
)
end

operators = %w(+ - * / % < <= > >= == != && || !)

punctuation = %w(( ) { } [ ] , : ; = .)

state :root do
mixin :comments

# Match strings
rule %r/'/, Str::Single, :string

# Match numbers
rule %r/\b\d+\b/, Num

# Rules for sets of reserved keywords
rule %r/\b\w+\b/ do |m|
if self.class.keywords.include? m[0]
token Keyword
elsif self.class.datatypes.include? m[0]
token Keyword::Type
elsif self.class.functions.include? m[0]
token Name::Function
else
token Name
end
end

# Match operators
rule %r/#{operators.map { |o| Regexp.escape(o) }.join('|')}/, Operator

# Enter a state when encountering an opening curly bracket
rule %r/{/, Punctuation::Indicator, :block

# Match punctuation
rule %r/#{punctuation.map { |p| Regexp.escape(p) }.join('|')}/, Punctuation

# Match identifiers
rule %r/[a-zA-Z_]\w*/, Name

# Match decorators
rule %r/@[a-zA-Z_]\w*/, Name::Decorator

# Ignore whitespace
rule %r/\s+/, Text
end

state :comments do
rule %r(//[^\n\r]+), Comment::Single
rule %r(/\*.*?\*/)m, Comment::Multiline
end

state :string do
rule %r/[^'$}]+/, Str::Single
rule %r/\$(?!\{)/, Str::Single
rule %r/\$[\{]/, Str::Interpol, :interp
rule %r/\'/, Str::Single, :pop!
rule %r/\$+/, Str::Single
end

state :interp do
rule %r/\}/, Str::Interpol, :pop!
mixin :root
end

# State for matching code blocks between curly brackets
state :block do
# Match property names
rule %r/\b([a-zA-Z_]\w*)\b(?=\s*:)/, Name::Property

# Match closing curly brackets
rule %r/}/, Punctuation::Indicator, :pop!

# Include the root state for nested tokens
mixin :root
end
end
end
end
16 changes: 16 additions & 0 deletions spec/lexers/bicep_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

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

describe 'guessing' do
include Support::Guessing

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

32 changes: 32 additions & 0 deletions spec/visual/samples/bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Target scope can be set using the following values:
- resourceGroup (default)
- subscription
- managementGroup
- tenant
*/
targetScope = 'subscription' // To create a resource group

@description('The Azure region to create the resources in.')
param location string

var suffix = uniqueString(subscription().subscriptionId, 'my-project')

var someTags = [
{
key: 'location'
value: location
}
{
key: 'isTest'
value: true
}
]

// Create a resource group
resource rg 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: 'rg-${suffix}'
location: location

tags: toObject(someTags, tag => tag.key, tag => tag.value)
}