Skip to content

Commit 6e11673

Browse files
committed
Add Picat lexer
1 parent bf007b7 commit 6e11673

File tree

4 files changed

+349
-0
lines changed

4 files changed

+349
-0
lines changed

lib/rouge/demos/picat

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module main.
2+
3+
import util.
4+
5+
% Calculate factorial using recursion
6+
fact(0) = 1.
7+
fact(N) = N * fact(N-1) => N > 0.
8+
9+
main =>
10+
X = 5,
11+
writef("Factorial of %d is %d\n", X, fact(X)),
12+
Sum = sum([1,2,3]),
13+
writef("Sum = %d\n", Sum).

lib/rouge/lexers/picat.rb

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# -*- coding: utf-8 -*- #
2+
# frozen_string_literal: true
3+
4+
module Rouge
5+
module Lexers
6+
class Picat < RegexLexer
7+
title "Picat"
8+
desc "The Picat programming language (picat-lang.org)"
9+
10+
tag 'picat'
11+
filenames '*.pi'
12+
mimetypes 'text/x-picat'
13+
14+
def self.keywords
15+
@keywords ||= Set.new %w(
16+
module import private table index
17+
if else elseif end foreach while do
18+
not fail true false
19+
catch try in
20+
repeat once var throw
21+
)
22+
end
23+
24+
def self.builtins
25+
@builtins ||= Set.new %w(
26+
append write writeln print println member length
27+
solve solve_all new_array new_map new_set
28+
min max sum prod floor ceiling round
29+
abs sqrt sin cos tan log exp
30+
open close printf get_heap_map get_global_map
31+
get_table_map instance final action solve
32+
)
33+
end
34+
35+
state :root do
36+
rule %r/\s+/m, Text
37+
rule %r/%.*$/, Comment::Single
38+
rule %r/\/\*/, Comment::Multiline, :multiline_comment
39+
40+
# Module declaration
41+
rule %r/(module)(\s+)([a-z][a-zA-Z0-9_]*)/m do
42+
groups Keyword::Namespace, Text::Whitespace, Name::Namespace
43+
end
44+
45+
# Import declaration
46+
rule %r/(import)(\s+)([a-z][a-zA-Z0-9_]*)/m do
47+
groups Keyword::Namespace, Text::Whitespace, Name::Namespace
48+
push :import_list
49+
end
50+
51+
# Handle bare 'import' without immediate module name
52+
rule %r/(import)(\s*$)/ do
53+
groups Keyword::Namespace, Text::Whitespace
54+
push :import_list
55+
end
56+
57+
# Numbers with underscore separators (must come before regular integers)
58+
rule %r/\d+(_\d+)+/, Num::Integer
59+
60+
# Other numbers
61+
rule %r/0[xX][0-9a-fA-F]+/, Num::Hex
62+
rule %r/0[oO][0-7]+/, Num::Oct
63+
rule %r/0[bB][01]+/, Num::Bin
64+
rule %r/[0-9]+\.[0-9]+([eE][+-]?[0-9]+)?/, Num::Float
65+
rule %r/[0-9]+/, Num::Integer
66+
67+
# Strings and Atoms
68+
rule %r/"(\\.|[^"])*"/, Str::Double
69+
rule %r/'(\\.|[^'])*'/, Str::Symbol # Quoted atoms
70+
71+
# Variables
72+
rule %r/[A-Z_][A-Za-z0-9_]*/, Name::Variable
73+
74+
# Keywords and builtins (moved before other identifier patterns)
75+
rule %r/[a-z][a-zA-Z0-9_]*(?=[^a-zA-Z0-9_])/ do |m|
76+
if self.class.keywords.include? m[0]
77+
token Keyword
78+
elsif self.class.builtins.include? m[0]
79+
token Name::Builtin
80+
else
81+
token Name
82+
end
83+
end
84+
85+
# Module-qualified names
86+
rule %r/([a-z][a-zA-Z0-9_]*)(\.)([a-z][a-zA-Z0-9_]*)/ do
87+
groups Name::Namespace, Punctuation, Name::Function
88+
end
89+
90+
# Structure notation
91+
rule %r/\$[a-z][a-zA-Z0-9_]*/, Name::Class
92+
93+
# Other identifiers
94+
rule %r/[a-z][a-zA-Z0-9_]*/, Name
95+
96+
# Import items (commas and periods)
97+
rule %r/,|\./, Punctuation
98+
99+
# Constraint operators
100+
rule %r/#=>|#<=>|#\/\\|#\\\/|#\^|#~|#=|#!=|#<|#=<|#<=|#>|#>=/, Operator
101+
102+
# Term comparison operators
103+
rule %r/@<|@=<|@<=|@>|@>=/, Operator
104+
105+
# DCG notation
106+
rule %r/-->/, Operator
107+
108+
# List comprehension separator
109+
rule %r/\s+:\s+/, Punctuation
110+
111+
# Range notation
112+
rule %r/\.\./, Operator
113+
114+
# List cons operator (|)
115+
rule %r/\|/, Punctuation
116+
117+
# Other operators and punctuation
118+
rule %r/=>|:=|\?=>|==|!=|=<|>=|::|\+\+|--|!|;|:|\.|=|<|>|\+|-|\*|\/|\[|\]|\{|\}|\(|\)|\$|@/, Operator
119+
120+
# Table/index declarations
121+
rule %r/(\+|-)(?=[\s,\)])/, Operator
122+
end
123+
124+
state :multiline_comment do
125+
rule %r/[^*\/]+/m, Comment::Multiline
126+
rule %r/\*\//, Comment::Multiline, :pop!
127+
rule %r/[\*\/]/, Comment::Multiline
128+
end
129+
130+
state :import_list do
131+
rule %r/\s+/m, Text::Whitespace
132+
rule %r/[a-z][a-zA-Z0-9_]*/, Name::Namespace
133+
rule %r/,/, Punctuation
134+
rule %r/\./, Punctuation, :pop!
135+
end
136+
end
137+
end
138+
end

spec/lexers/picat_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# -*- coding: utf-8 -*- #
2+
# frozen_string_literal: true
3+
4+
describe Rouge::Lexers::Picat do
5+
let(:subject) { Rouge::Lexers::Picat.new }
6+
7+
describe 'guessing' do
8+
include Support::Guessing
9+
10+
it 'guesses by filename' do
11+
assert_guess :filename => 'foo.pi'
12+
end
13+
14+
it 'guesses by mimetype' do
15+
assert_guess :mimetype => 'text/x-picat'
16+
end
17+
end
18+
end

spec/visual/samples/picat

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/* Picat Language Sample
2+
Demonstrating syntax highlighting features */
3+
4+
module example.
5+
6+
import
7+
util,
8+
cp.
9+
10+
% Different number formats
11+
Nums = [
12+
1_000_000, % Underscore separator
13+
0xFFF, % Hexadecimal
14+
0o777, % Octal
15+
0b1010, % Binary
16+
1.23e-4, % Scientific notation
17+
3.14159 % Float
18+
].
19+
20+
% String escape sequences
21+
Strings = [
22+
"Double \"quoted\" string",
23+
"Line 1\nLine 2",
24+
"Tab\tafter",
25+
"Unicode \u0041",
26+
'Single \'quoted\' atom'
27+
].
28+
29+
% Special characters in atoms
30+
atom_1,
31+
'atom-with-dashes',
32+
'atom with spaces',
33+
'atom@with@special@chars'.
34+
35+
% Special operators
36+
X #=> Y, % Constraint implication
37+
X #<=> Y, % Constraint equivalence
38+
X #/\ Y, % Constraint AND
39+
X #\/ Y, % Constraint OR
40+
X #^ Y, % Constraint XOR
41+
#~ X, % Constraint NOT
42+
43+
% Special comparison operators
44+
X @< Y, % Term comparison less than
45+
X @=< Y, % Term comparison less than or equal
46+
X @> Y, % Term comparison greater than
47+
X @>= Y, % Term comparison greater than or equal
48+
49+
% Range notation
50+
List1 = 1..10, % Range with step 1
51+
List2 = 1..2..10, % Range with step 2
52+
53+
% Special built-in predicates
54+
once(Goal),
55+
repeat,
56+
true,
57+
fail,
58+
59+
% Assignment vs unification
60+
X = Y, % Unification
61+
X := Y, % Assignment
62+
X == Y, % Term equality
63+
X !== Y, % Term inequality
64+
X =:= Y, % Arithmetic equality
65+
66+
% Special list/array access
67+
List = [1,2,3,4,5],
68+
First = List[1],
69+
Slice = List[2..4],
70+
Array = {1,2,3,4,5},
71+
AFirst = Array[1],
72+
ASlice = Array[2..4].
73+
74+
% DCG notation (since version 3.0)
75+
sentence --> noun_phrase, verb_phrase.
76+
77+
% Variables and lists
78+
process_list(List) =>
79+
[Head|Tail] = List,
80+
foreach(X in Tail)
81+
if X > Head then
82+
println(X)
83+
elseif X < Head then
84+
println("Less")
85+
else
86+
println("Equal")
87+
end
88+
end.
89+
90+
% Using built-in functions
91+
math_ops =>
92+
A = abs(-42),
93+
B = sqrt(2),
94+
C = sin(3.14159),
95+
D = new_array(3),
96+
Map = new_map(),
97+
Set = new_set([1,2,3]).
98+
99+
% Pattern matching and operators
100+
compare(X, Y) ?=>
101+
X >= Y,
102+
X =< 100,
103+
X != Y,
104+
X :: 1..10.
105+
compare(_, _) => fail.
106+
107+
% Solving example
108+
solve_puzzle =>
109+
Vars = [X,Y,Z],
110+
Vars :: 1..9,
111+
sum(Vars) #= 15,
112+
solve(Vars).
113+
114+
% Functions with returns
115+
factorial(0) = 1.
116+
factorial(N) = F => F = N * factorial(N - 1).
117+
118+
% List comprehensions
119+
S = [X*X : X in 1..20, X mod 2 = 0].
120+
121+
% Index on either first or second argument
122+
index (+,-) (-,+)
123+
edge(a,b).
124+
edge(a,c).
125+
edge(b,c).
126+
edge(c,b).
127+
128+
% Private tabled function
129+
private
130+
table
131+
fibonacci(0) = 1.
132+
fibonacci(1) = 1.
133+
fibonacci(N) = F => F = fibonacci(N-1) + fibonacci(N-2).
134+
135+
% Structures
136+
print_books =>
137+
B1 = $book("Dune", "Frank Herbert"),
138+
println(B1[1]), % Access first element
139+
B2 = new_map([title = "Dune", author = "Frank Herbert"]),
140+
println(B2.get(title)).
141+
142+
% Action rules for event-driven programming
143+
echo(X,Flag), var(Flag), {event(X,T)} =>
144+
writeln(T).
145+
echo(_X,_Flag) =>
146+
writeln(done).
147+
148+
% Exception handling
149+
divide(A, B) =>
150+
catch(
151+
(C = A / B),
152+
error(zero_divisor, _),
153+
println("Division by zero")
154+
).
155+
156+
% Pattern matching with as-patterns (@)
157+
process(L@[H|T]) =>
158+
println(L), % whole list
159+
println(H), % head
160+
println(T). % tail
161+
162+
% Pattern matching with disjunction
163+
match_guard(X) ?=>
164+
X = 1;
165+
X = 2;
166+
X = 3.
167+
168+
% Using the planner module
169+
import planner.
170+
final([n,n,n,n]) => true. % Example goal state
171+
action(S,NextS,Action,Cost) => ... % Define possible actions
172+
173+
% Basic I/O
174+
write_data =>
175+
FD = open("data.txt", write),
176+
foreach(I in 1..10)
177+
printf(FD, "%d\n", I)
178+
end,
179+
close(FD).
180+

0 commit comments

Comments
 (0)