diff --git a/lib/rouge/demos/natural b/lib/rouge/demos/natural new file mode 100644 index 0000000000..30454db0ab --- /dev/null +++ b/lib/rouge/demos/natural @@ -0,0 +1,20 @@ +* > Natural Program: SAMPLE +DEFINE DATA LOCAL + 01 #I (I4) 01 #S (A10) INIT <'TESTE'> + 01 VIEW-EMP VIEW OF EMPLOYEES + 02 NAME 02 SALARY (1) +END-DEFINE +ON ERROR WRITE 'ERRO:' *ERROR-NR ESCAPE ROUTINE END-ERROR +FORMAT PS=0 LS=80 +FOR #I = 1 TO 5 + FIND VIEW-EMP WITH NAME = 'SMITH' WHERE SALARY(1) > 5000 + DECIDE ON FIRST VALUE OF #S + VALUE 'TESTE' PERFORM SUB-LOG + NONE IGNORE + END-DECIDE + COMPUTE SALARY(1) = SALARY(1) * 1.1; UPDATE; END TRANSACTION + DISPLAY 'FUNC:' NAME 'NOVO SAL:' SALARY(1) 'DATA:' *DAT4U + END-FIND +END-FOR +DEFINE SUBROUTINE SUB-LOG WRITE 'LOG:' *USER #S END-SUBROUTINE +END /* End of the program */ \ No newline at end of file diff --git a/lib/rouge/lexers/natural.rb b/lib/rouge/lexers/natural.rb new file mode 100644 index 0000000000..63d962750b --- /dev/null +++ b/lib/rouge/lexers/natural.rb @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +module Rouge + module Lexers + class Natural < RegexLexer + title 'Natural' + desc 'Natural is a programming language developed by Software AG for the Adabas database' + tag 'natural' + filenames '*.NSA', '*.NSC', '*.NSD', '*.NSF', '*.NSG', '*.NSH', '*.NSL', '*.NSM', '*.NSN', '*.NSP', '*.NSS', '*.NST', '*.NSR', '*.NS7' + mimetypes 'text/x-natural' + + identifier = /[a-zA-Z0-9-]+/ + + # Palavras-chave extraídas do seu repositório tmLanguage + def self.keywords + @keywords ||= Set.new(%w( + ADD ASSIGN AT CALL CALLNAT CLOSE COMPUTE COMPRESS DECIDE DEFINE + DELETE DISPLAY DIVIDE DO ELSE END END-DECIDE END-DEFINE END-FIND + END-FOR END-FUNCTION END-IF END-NOREC END-READ END-REPEAT + END-SUBROUTINE ESCAPE EXAMINE FETCH FIND FOR FORMAT GET + HISTOGRAM IF INPUT MOVE MULTIPLY PERFORM PRINT READ REPEAT + RESET REINPUT RETURN SELECT SET SKIP STOP STORE SUBTRACT + TERMINATE UPDATE VALUE WHEN WHILE WRITE + )) + end + + # Palavras-chave específicas de definição de dados + def self.data_keywords + @data_keywords ||= Set.new(%w( + WITH VIEW VALUE RESULT REDEFINE PARAMETER OPTIONAL OF OBJECT + LOCAL INIT INDEPENDENT GLOBAL DYNAMIC CONTEXT CONSTANT CONST BY + )) + end + + # Operadores lógicos e aritméticos + def self.operators + @operators ||= Set.new(%w( + THRU OR NOTEQUAL NOT NE LT LE GT GE EQ AND BUT MASK SCAN + )) + end + + state :root do + # Comentários: Suporta ^* e /* + rule %r/(^\*.*$|\/\*.*$)/, Comment::Single + + # Variáveis de Sistema: Iniciadas com * + rule %r/(?i)\*(winmgrvers|username|user|tpvers|time|timn|program|pid|pf-key|pagesize|page-number|osvers|opsys|number|natvers|isn|init-user|hostname|error-nr|error-line|datx|datn|date|counter|applic-id)/, Name::Variable::Global + + # Variáveis locais iniciadas com # + rule %r/\#[a-zA-Z0-9-]+/, Name::Variable + + # Strings + rule %r/'/, Str::Single, :string_single + rule %r/"/, Str::Double, :string_double + + # Datas e Horas: EX: D'2023-10-25' + rule %r/([tTdDeE]?)\'[0-9:\/\.\-]+\'/, Literal::Date + + # Números + rule %r/(?<=:=]/, Operator + + # Pontuação + rule %r/[.,;:()]/, Punctuation + + # Whitespace + rule %r/\s+/, Text::Whitespace + end + + state :string_single do + rule %r/[^'\n]+/, Str::Single + rule %r/''/, Str::Escape + rule %r/'/, Str::Single, :pop! + rule %r/\n/, Error + end + + state :string_double do + rule %r/[^"\n]+/, Str::Double + rule %r/""/, Str::Escape + rule %r/"/, Str::Double, :pop! + rule %r/\n/, Error + end + end + end +end diff --git a/spec/lexers/natural_spec.rb b/spec/lexers/natural_spec.rb new file mode 100644 index 0000000000..e24931b03d --- /dev/null +++ b/spec/lexers/natural_spec.rb @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- # +# frozen_string_literal: true + +describe Rouge::Lexers::Natural do + let(:subject) { Rouge::Lexers::Natural.new } + + describe 'guessing' do + include Support::Guessing + + it 'guesses by filename' do + assert_guess :filename => 'foo.NSA' + assert_guess :filename => 'foo.NSC' + assert_guess :filename => 'foo.NSD' + assert_guess :filename => 'foo.NSF' + assert_guess :filename => 'foo.NSG' + assert_guess :filename => 'foo.NSH' + assert_guess :filename => 'foo.NSL' + assert_guess :filename => 'foo.NSM' + assert_guess :filename => 'foo.NSN' + assert_guess :filename => 'foo.NSP' + assert_guess :filename => 'foo.NSS' + assert_guess :filename => 'foo.NST' + assert_guess :filename => 'foo.NSR' + assert_guess :filename => 'foo.NS7' + end + it 'guesses by mimetype' do + assert_guess :mimetype => 'text/x-natural' + end + end +end \ No newline at end of file diff --git a/spec/visual/samples/natural b/spec/visual/samples/natural new file mode 100644 index 0000000000..24a8f58ef6 --- /dev/null +++ b/spec/visual/samples/natural @@ -0,0 +1,85 @@ +* > Natural Program: EXEMPLO +* > Description: Demonstração exaustiva de sintaxe e comandos +************************************************************************ +DEFINE DATA /* Início do bloco de dados [cite: 5] */ +GLOBAL USING GCOMUM /* Referência a área de dados global */ +LOCAL /* Variáveis locais do programa */ + 01 #MATRICULA (N8) /* Numérico de 8 dígitos */ + 01 #NOME-COMPLETO (A50) /* Alfanumérico de 50 posições */ + 01 #DATA-PROCESS (D) /* Tipo Data */ + 01 #STATUS-VAR (A1) INIT <'A'> /* Inicialização de variável */ + 01 #I (I4) /* Inteiro de 4 bytes */ + + /* Definição de uma View para acesso ao banco */ + 01 EMPREGADOS VIEW OF EMPLOYEES + 02 NAME + 02 FIRST-NAME + 02 SALARY (1:2) /* Campo múltiplo (Array) */ + 02 DEPT-CODE + + /* Redefinição de variável [cite: 6] */ + 01 #DATA-RED (A8) + 01 REDEFINE #DATA-RED + 02 #ANO (A4) + 02 #MES (A2) + 02 #DIA (A2) + +END-DEFINE /* Fim da definição de dados [cite: 5] */ + +* ---------------------------------------------------------------------- +* TRATAMENTO DE ERROS ON-LINE +* ---------------------------------------------------------------------- +ON ERROR + WRITE 'ERRO SISTEMA:' *ERROR-NR ' NA LINHA:' *ERROR-LINE + ESCAPE ROUTINE +END-ERROR + +* ---------------------------------------------------------------------- +* LÓGICA DE PROCESSAMENTO +* ---------------------------------------------------------------------- +FORMAT PS=60 LS=120 /* Configura página e linha */ +#DATA-PROCESS := *DATX /* Variável de sistema para data */ + +/* Comando de repetição simples */ +FOR #I = 1 TO 10 STEP 1 + IF #I > 5 + IGNORE + ELSE + PRINT 'ITERAÇÃO:' #I + END-IF +END-FOR + +/* Busca no Banco de Dados Adabas */ +FIND EMPREGADOS WITH NAME = 'SMITH' + WHERE DEPT-CODE = 'FINA' + + /* Comando de decisão múltipla */ + DECIDE ON FIRST VALUE OF #STATUS-VAR + VALUE 'A' + PERFORM PROCESSAR-ATIVO /* Chamada de sub-rotina interna */ + VALUE 'I' + IGNORE + NONE + REINPUT 'STATUS INVÁLIDO' MARK *#STATUS-VAR + END-DECIDE + + /* Manipulação de Strings */ + COMPRESS FIRST-NAME NAME INTO #NOME-COMPLETO LEAVING NO SPACE + + /* Cálculo Aritmético */ + COMPUTE SALARY(1) = SALARY(1) * 1.10 + + UPDATE /* Atualiza registro no banco */ + END TRANSACTION /* Confirma alteração */ + + DISPLAY 'NOME:' #NOME-COMPLETO (AL=30) + 'SALÁRIO ATUALIZADO:' SALARY(1) + +END-FIND /* Fim do loop FIND */ + +/* Sub-rotina Interna */ +DEFINE SUBROUTINE PROCESSAR-ATIVO + WRITE 'PROCESSANDO REGISTRO ATIVO DE:' *USER +END-SUBROUTINE + +END /* Encerramento do programa */ \ No newline at end of file