diff --git a/lib/rouge/demos/ttcn3 b/lib/rouge/demos/ttcn3 new file mode 100644 index 0000000000..969107de42 --- /dev/null +++ b/lib/rouge/demos/ttcn3 @@ -0,0 +1,6 @@ +module DNSTester { + type integer Identification( 0..65535 ); // 16-bit integer + type enumerated MessageKind {e_Question, e_Answer}; + type charstring Question; + type charstring Answer; +} diff --git a/lib/rouge/lexers/ttcn3.rb b/lib/rouge/lexers/ttcn3.rb new file mode 100644 index 0000000000..4b8b27d35c --- /dev/null +++ b/lib/rouge/lexers/ttcn3.rb @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +module Rouge + module Lexers + class TTCN3 < RegexLexer + title "TTCN3" + desc "The TTCN3 programming language (ttcn-3.org)" + + tag 'ttcn3' + filenames '*.ttcn', '*.ttcn3' + mimetypes 'text/x-ttcn3', 'text/x-ttcn' + + def self.keywords + @keywords ||= %w( + module import group type port component signature external + execute const template function altstep testcase var timer if + else select case for while do label goto start stop return + break int2char int2unichar int2bit int2enum int2hex int2oct + int2str int2float float2int char2int char2oct unichar2int + unichar2oct bit2int bit2hex bit2oct bit2str hex2int hex2bit + hex2oct hex2str oct2int oct2bit oct2hex oct2str oct2char + oct2unichar str2int str2hex str2oct str2float enum2int + any2unistr lengthof sizeof ispresent ischosen isvalue isbound + istemplatekind regexp substr replace encvalue decvalue + encvalue_unichar decvalue_unichar encvalue_o decvalue_o + get_stringencoding remove_bom rnd hostid send receive + setverdict + ) + end + + def self.reserved + @reserved ||= %w( + all alt apply assert at configuration conjunct const control + delta deterministic disjunct duration fail finished fuzzy from + history implies inconc inv lazy mod mode notinv now omit + onentry onexit par pass prev realtime seq setstate static + stepsize stream timestamp until values wait + ) + end + + def self.types + @types ||= %w( + anytype address boolean bitstring charstring hexstring octetstring + component enumerated float integer port record set of union universal + ) + end + + # optional comment or whitespace + ws = %r((?:\s|//.*?\n|/[*].*?[*]/)+) + id = /[a-zA-Z_]\w*/ + digit = /\d_+\d|\d/ + bin_digit = /[01]_+[01]|[01]/ + oct_digit = /[0-7]_+[0-7]|[0-7]/ + hex_digit = /\h_+\h|\h/ + + state :statements do + rule %r/\n+/m, Text + rule %r/[ \t\r]+/, Text + rule %r/\\\n/, Text # line continuation + + rule %r(//(\\.|.)*?$), Comment::Single + rule %r(/(\\\n)?[*].*?[*](\\\n)?/)m, Comment::Multiline + + rule %r/"/, Str, :string + rule %r/'(?:\\.|[^\\]|\\u[0-9a-f]{4})'/, Str::Char + + rule %r/#{digit}+\.#{digit}+([eE]#{digit}+)?[fd]?/i, Num::Float + rule %r/'#{bin_digit}+'B/i, Num::Bin + rule %r/'#{hex_digit}+'H/i, Num::Hex + rule %r/'#{oct_digit}+'O/i, Num::Oct + rule %r/#{digit}+/i, Num::Integer + + rule %r([~!%^&*+:=\|?<>/-]), Operator + rule %r/[()\[\]{},.;:]/, Punctuation + + rule %r/(?:true|false|null)\b/, Name::Builtin + + rule id do |m| + name = m[0] + if self.class.keywords.include? name + token Keyword + elsif self.class.types.include? name + token Keyword::Type + elsif self.class.reserved.include? name + token Keyword::Reserved + else + token Name + end + end + end + + state :root do + rule %r/module\b/, Keyword::Declaration, :module + rule %r/import\b/, Keyword::Namespace, :import + + mixin :statements + end + + state :string do + rule %r/"/, Str, :pop! + rule %r/\\([\\abfnrtv"']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})/, Str::Escape + rule %r/[^\\"\n]+/, Str + rule %r/\\\n/, Str + rule %r/\\/, Str # stray backslash + end + + state :module do + rule %r/\s+/m, Text + rule id, Name::Class, :pop! + end + + state :import do + rule %r/\s+/m, Text + rule %r/[\w.]+\*?/, Name::Namespace, :pop! + end + end + end +end diff --git a/spec/lexers/ttcn3_spec.rb b/spec/lexers/ttcn3_spec.rb new file mode 100644 index 0000000000..1acad980f1 --- /dev/null +++ b/spec/lexers/ttcn3_spec.rb @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +describe Rouge::Lexers::TTCN3 do + let(:subject) { Rouge::Lexers::TTCN3.new } + + describe 'guessing' do + include Support::Guessing + + it 'guesses by filename' do + assert_guess :filename => 'foo.ttcn' + assert_guess :filename => 'foo.ttcn3' + end + + it 'guesses by mimetype' do + assert_guess :mimetype => 'text/x-ttcn' + assert_guess :mimetype => 'text/x-ttcn3' + end + end +end diff --git a/spec/visual/samples/ttcn3 b/spec/visual/samples/ttcn3 new file mode 100644 index 0000000000..034926f0d9 --- /dev/null +++ b/spec/visual/samples/ttcn3 @@ -0,0 +1,120 @@ +/* ---------------------------------------------------------------------------- + * (c) Copyright Wiley & Sons 2005 + * + * @author: Colin Willcock, Thomas Deiß, Stephan Tobies, Stefan Keil, Federico + * Engler, Stephan Schulz + * @desc: This is a strongly simplified Domain Name Server (DNS) test suite + * for testing some basic domain name resolution behaviour. + * @remark: This TTCN-3 code is based on the DNS example code presented in + * "C. Willock et al., An Introduction to TTCN-3, Wiley & Sons, 2005. + * ISBN: 0-470-01224-2" + * This copyright notice shall not be removed in copies of this file. + * ---------------------------------------------------------------------------- +*/ +module DNSTester { + + // Simple type definitions to match the protocol structure + type integer Identification( 0..65535 ); // 16-bit integer + type enumerated MessageKind {e_Question, e_Answer}; + type charstring Question; + type charstring Answer; + + // The definition of our DNS message type. + type record DNSMessage { + Identification identification, + MessageKind messageKind, + Question question, + Answer answer optional + } + + // A possible template for the DNS message type. + template DNSMessage a_NokiaQuestion := { + identification := 12345, + messageKind := e_Question, + question := "www.nokia.com", + answer := omit + } + + // A parameterized template for DNS questions based on DNSMessage. + template DNSMessage a_DNSQuestion( Identification p_id, Question p_question ) := { + identification := p_id, + messageKind := e_Question, + question := p_question, + answer := omit + } + + // A parameterized template for DNS answers based on DNSMessage. + template DNSMessage a_DNSAnswer( Identification p_id, Answer p_answer ) := { + identification := p_id, + messageKind := e_Answer, + question := ?, + answer := p_answer + } + + // DNS messages are allowed to move in and out through ports of this type. + type port DNSPort message { + inout DNSMessage + } + + // Our single component uses one single port to communicate with the SUT. + type component DNSClient { + port DNSPort serverPort + } + + // Our first test case! This small test case will behave very poorly in case + // of an erroneous SUT. More about this later! + testcase ExampleResolveNokia1() runs on DNSClient { + serverPort.send( a_DNSQuestion( 12345, "www.research.nokia.com" ) ); + serverPort.receive( a_DNSAnswer( 12345, "172.21.56.98" ) ); + setverdict( pass ); + stop; + } + + testcase ExampleResolveNokia2() runs on DNSClient { + serverPort.send( a_DNSQuestion( 12345, "www.research.nokia.com" ) ); + alt { + // Handle the case when the expected answer comes in. + [] serverPort.receive( a_DNSAnswer( 12345, "172.21.56.98" ) ) { + setverdict( pass ); + } + // Handle the case when unexpected answers come in. + [] serverPort.receive { + setverdict( fail ); + } + } + stop; + } + + // Our test case is now able to handle incorrect replies as well as + // missing replies. + testcase ExampleResolveNokia3() runs on DNSClient { + timer replyTimer; + serverPort.send( a_DNSQuestion( 12345, "www.research.nokia.com" ) ); + replyTimer.start( 20.0 ); + alt { + // Handle the case when the expected answer comes in. + [] serverPort.receive( a_DNSAnswer( 12345, "172.21.56.98" ) ) { + setverdict( pass ); + replyTimer.stop; + } + // Handle the case when unexpected answers come in. + [] serverPort.receive { + setverdict( fail ); + replyTimer.stop; + } + // Handle the case when no answer comes in. + [] replyTimer.timeout { + setverdict( fail ); + } + } + stop; + } + + // Our small control part. + control { + execute( ExampleResolveNokia1() ); + execute( ExampleResolveNokia2() ); + execute( ExampleResolveNokia3() ); + } + +}