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 Picat lexer #2110

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
13 changes: 13 additions & 0 deletions lib/rouge/demos/picat
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module main.

import util.

% Calculate factorial using recursion
fact(0) = 1.
fact(N) = N * fact(N-1) => N > 0.

main =>
X = 5,
writef("Factorial of %d is %d\n", X, fact(X)),
Sum = sum([1,2,3]),
writef("Sum = %d\n", Sum).
138 changes: 138 additions & 0 deletions lib/rouge/lexers/picat.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
module Lexers
class Picat < RegexLexer
title "Picat"
desc "The Picat programming language (picat-lang.org)"

tag 'picat'
filenames '*.pi'
mimetypes 'text/x-picat'

def self.keywords
@keywords ||= Set.new %w(
module import private table index
if else elseif end foreach while do
not fail true false
catch try in
repeat once var throw
)
end

def self.builtins
@builtins ||= Set.new %w(
append write writeln print println member length
solve solve_all new_array new_map new_set
min max sum prod floor ceiling round
abs sqrt sin cos tan log exp
open close printf get_heap_map get_global_map
get_table_map instance final action solve
)
end

state :root do
rule %r/\s+/m, Text
rule %r/%.*$/, Comment::Single
rule %r/\/\*/, Comment::Multiline, :multiline_comment

# Module declaration
rule %r/(module)(\s+)([a-z][a-zA-Z0-9_]*)/m do
groups Keyword::Namespace, Text::Whitespace, Name::Namespace
end

# Import declaration
rule %r/(import)(\s+)([a-z][a-zA-Z0-9_]*)/m do
groups Keyword::Namespace, Text::Whitespace, Name::Namespace
push :import_list
end

# Handle bare 'import' without immediate module name
rule %r/(import)(\s*$)/ do
groups Keyword::Namespace, Text::Whitespace
push :import_list
end

# Numbers with underscore separators (must come before regular integers)
rule %r/\d+(_\d+)+/, Num::Integer

# Other numbers
rule %r/0[xX][0-9a-fA-F]+/, Num::Hex
rule %r/0[oO][0-7]+/, Num::Oct
rule %r/0[bB][01]+/, Num::Bin
rule %r/[0-9]+\.[0-9]+([eE][+-]?[0-9]+)?/, Num::Float
rule %r/[0-9]+/, Num::Integer

# Strings and Atoms
rule %r/"(\\.|[^"])*"/, Str::Double
rule %r/'(\\.|[^'])*'/, Str::Symbol # Quoted atoms

# Variables
rule %r/[A-Z_][A-Za-z0-9_]*/, Name::Variable

# Keywords and builtins (moved before other identifier patterns)
rule %r/[a-z][a-zA-Z0-9_]*(?=[^a-zA-Z0-9_])/ do |m|
if self.class.keywords.include? m[0]
token Keyword
elsif self.class.builtins.include? m[0]
token Name::Builtin
else
token Name
end
end

# Module-qualified names
rule %r/([a-z][a-zA-Z0-9_]*)(\.)([a-z][a-zA-Z0-9_]*)/ do
groups Name::Namespace, Punctuation, Name::Function
end

# Structure notation
rule %r/\$[a-z][a-zA-Z0-9_]*/, Name::Class

# Other identifiers
rule %r/[a-z][a-zA-Z0-9_]*/, Name

# Import items (commas and periods)
rule %r/,|\./, Punctuation

# Constraint operators
rule %r/#=>|#<=>|#\/\\|#\\\/|#\^|#~|#=|#!=|#<|#=<|#<=|#>|#>=/, Operator

# Term comparison operators
rule %r/@<|@=<|@<=|@>|@>=/, Operator

# DCG notation
rule %r/-->/, Operator

# List comprehension separator
rule %r/\s+:\s+/, Punctuation

# Range notation
rule %r/\.\./, Operator

# List cons operator (|)
rule %r/\|/, Punctuation

# Other operators and punctuation
rule %r/=>|:=|\?=>|==|!=|=<|>=|::|\+\+|--|!|;|:|\.|=|<|>|\+|-|\*|\/|\[|\]|\{|\}|\(|\)|\$|@/, Operator

# Table/index declarations
rule %r/(\+|-)(?=[\s,\)])/, Operator
end

state :multiline_comment do
rule %r/[^*\/]+/m, Comment::Multiline
rule %r/\*\//, Comment::Multiline, :pop!
rule %r/[\*\/]/, Comment::Multiline
end

state :import_list do
rule %r/\s+/m, Text::Whitespace
rule %r/[a-z][a-zA-Z0-9_]*/, Name::Namespace
rule %r/,/, Punctuation
rule %r/\./, Punctuation, :pop!
end
end
end
end
18 changes: 18 additions & 0 deletions spec/lexers/picat_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::Picat do
let(:subject) { Rouge::Lexers::Picat.new }

describe 'guessing' do
include Support::Guessing

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

it 'guesses by mimetype' do
assert_guess :mimetype => 'text/x-picat'
end
end
end
179 changes: 179 additions & 0 deletions spec/visual/samples/picat
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/* Picat Language Sample
Demonstrating syntax highlighting features */

module example.

import
util,
cp.

% Different number formats
Nums = [
1_000_000, % Underscore separator
0xFFF, % Hexadecimal
0o777, % Octal
0b1010, % Binary
1.23e-4, % Scientific notation
3.14159 % Float
].

% String escape sequences
Strings = [
"Double \"quoted\" string",
"Line 1\nLine 2",
"Tab\tafter",
"Unicode \u0041",
'Single \'quoted\' atom'
].

% Special characters in atoms
atom_1,
'atom-with-dashes',
'atom with spaces',
'atom@with@special@chars'.

% Special operators
X #=> Y, % Constraint implication
X #<=> Y, % Constraint equivalence
X #/\ Y, % Constraint AND
X #\/ Y, % Constraint OR
X #^ Y, % Constraint XOR
#~ X, % Constraint NOT

% Special comparison operators
X @< Y, % Term comparison less than
X @=< Y, % Term comparison less than or equal
X @> Y, % Term comparison greater than
X @>= Y, % Term comparison greater than or equal

% Range notation
List1 = 1..10, % Range with step 1
List2 = 1..2..10, % Range with step 2

% Special built-in predicates
once(Goal),
repeat,
true,
fail,

% Assignment vs unification
X = Y, % Unification
X := Y, % Assignment
X == Y, % Term equality
X !== Y, % Term inequality
X =:= Y, % Arithmetic equality

% Special list/array access
List = [1,2,3,4,5],
First = List[1],
Slice = List[2..4],
Array = {1,2,3,4,5},
AFirst = Array[1],
ASlice = Array[2..4].

% DCG notation (since version 3.0)
sentence --> noun_phrase, verb_phrase.

% Variables and lists
process_list(List) =>
[Head|Tail] = List,
foreach(X in Tail)
if X > Head then
println(X)
elseif X < Head then
println("Less")
else
println("Equal")
end
end.

% Using built-in functions
math_ops =>
A = abs(-42),
B = sqrt(2),
C = sin(3.14159),
D = new_array(3),
Map = new_map(),
Set = new_set([1,2,3]).

% Pattern matching and operators
compare(X, Y) ?=>
X >= Y,
X =< 100,
X != Y,
X :: 1..10.
compare(_, _) => fail.

% Solving example
solve_puzzle =>
Vars = [X,Y,Z],
Vars :: 1..9,
sum(Vars) #= 15,
solve(Vars).

% Functions with returns
factorial(0) = 1.
factorial(N) = F => F = N * factorial(N - 1).

% List comprehensions
S = [X*X : X in 1..20, X mod 2 = 0].

% Index on either first or second argument
index (+,-) (-,+)
edge(a,b).
edge(a,c).
edge(b,c).
edge(c,b).

% Private tabled function
private
table
fibonacci(0) = 1.
fibonacci(1) = 1.
fibonacci(N) = F => F = fibonacci(N-1) + fibonacci(N-2).

% Structures
print_books =>
B1 = $book("Dune", "Frank Herbert"),
println(B1[1]), % Access first element
B2 = new_map([title = "Dune", author = "Frank Herbert"]),
println(B2.get(title)).

% Action rules for event-driven programming
echo(X,Flag), var(Flag), {event(X,T)} =>
writeln(T).
echo(_X,_Flag) =>
writeln(done).

% Exception handling
divide(A, B) =>
catch(
(C = A / B),
error(zero_divisor, _),
println("Division by zero")
).

% Pattern matching with as-patterns (@)
process(L@[H|T]) =>
println(L), % whole list
println(H), % head
println(T). % tail

% Pattern matching with disjunction
match_guard(X) ?=>
X = 1;
X = 2;
X = 3.

% Using the planner module
import planner.
final([n,n,n,n]) => true. % Example goal state
action(S,NextS,Action,Cost) => ... % Define possible actions

% Basic I/O
write_data =>
FD = open("data.txt", write),
foreach(I in 1..10)
printf(FD, "%d\n", I)
end,
close(FD).