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
20 changes: 20 additions & 0 deletions lib/rouge/demos/natural
Original file line number Diff line number Diff line change
@@ -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 */
108 changes: 108 additions & 0 deletions lib/rouge/lexers/natural.rb
Original file line number Diff line number Diff line change
@@ -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/(?<![\w-])[+-]?[0-9]+[,.]?[0-9]*(?![\w-])/, Num

# Definições de Tipo (A20), (N10.2)
rule %r/(?<=\()(?i:[abu][0-9]*|[np][0-9\.,]+|i0*[148]|f0*[48]|[cdlt])/, Keyword::Type

# Níveis de variável no DEFINE DATA (Ex: 01, 02)
rule %r/^\s*[0-9]+/, Name::Label

# Identificadores (Keywords vs Names)
rule %r/#{identifier}/ do |m|
name = m[0].upcase
if self.class.keywords.include?(name)
token Keyword
elsif self.class.data_keywords.include?(name)
token Keyword::Declaration
elsif self.class.operators.include?(name)
token Operator::Word
else
token Name
end
end

# Operadores simbólicos
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
30 changes: 30 additions & 0 deletions spec/lexers/natural_spec.rb
Original file line number Diff line number Diff line change
@@ -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
85 changes: 85 additions & 0 deletions spec/visual/samples/natural
Original file line number Diff line number Diff line change
@@ -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 */