Skip to content

Commit

Permalink
Merge pull request #403 from jostrzol/master
Browse files Browse the repository at this point in the history
Support for zig language
  • Loading branch information
terryyin authored Jan 9, 2025
2 parents d896fb8 + 349ea70 commit a46563d
Show file tree
Hide file tree
Showing 9 changed files with 283 additions and 4 deletions.
2 changes: 2 additions & 0 deletions lizard_languages/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
''' programming languages of lizard '''

from lizard_languages.zig import ZigReader
from .clike import CLikeReader
from .erlang import ErlangReader
from .java import JavaReader
Expand Down Expand Up @@ -44,6 +45,7 @@ def languages():
KotlinReader,
SolidityReader,
ErlangReader,
ZigReader,
]


Expand Down
2 changes: 1 addition & 1 deletion lizard_languages/clike.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def _state_dec(self, token):
else:
self.next(self._state_global)
elif len(self.bracket_stack) == 1:
if token != 'void': # void is a reserved keyword, meaning no parameters
if token != 'void': # void is a reserved keyword, meaning no parameters
self.context.parameter(token)
return
self.context.add_to_long_function_name(token)
Expand Down
5 changes: 4 additions & 1 deletion lizard_languages/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import re
from .code_reader import CodeStateMachine, CodeReader


class FortranCommentsMixin:
@staticmethod
def get_comment_from_token(token):
if token.startswith('!'):
return token[1:]


class FortranReader(CodeReader, FortranCommentsMixin):
'''This is the reader for Fortran.'''

Expand Down Expand Up @@ -76,6 +78,7 @@ def preprocess(self, tokens):
elif not token.isspace() or token == '\n':
yield token


class FortranStates(CodeStateMachine):
_ends = re.compile('|'.join(r'END\s*{0}'.format(_) for _ in FortranReader._blocks), re.I)

Expand Down Expand Up @@ -255,4 +258,4 @@ def _module_or_procedure(self, token):
self._state = self._procedure
else:
self._state = self._module
self._module(token)
self._module(token)
2 changes: 1 addition & 1 deletion lizard_languages/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, context):
self.indents = [0]
self.context = context

def set_nesting(self, spaces, token = ""):
def set_nesting(self, spaces, token=""):
while self.indents[-1] > spaces and (not token.startswith(")")):
self.indents.pop()
self.context.pop_nesting()
Expand Down
25 changes: 25 additions & 0 deletions lizard_languages/zig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Language parser for Zig
"""

from __future__ import annotations

from .clike import CCppCommentsMixin
from .code_reader import CodeReader
from .golike import GoLikeStates


class ZigReader(CodeReader, CCppCommentsMixin):
ext = ["zig"]
language_names = ["zig"]
_conditions = {"if", "for", "while", "and", "or", "orelse", "try", "catch", "=>"}

def __init__(self, context):
super().__init__(context)
self.parallel_states = [ZigStates(context)]


class ZigStates(GoLikeStates):
FUNC_KEYWORD = "fn"

_type_definition = GoLikeStates._state_global
2 changes: 1 addition & 1 deletion pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ docstring-min-length=-1
[FORMAT]

# Maximum number of characters on a single line.
max-line-length=80
max-line-length=120

# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
Expand Down
3 changes: 3 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ universal = 1

[tool:pytest]
python_files = test*

[pycodestyle]
max-line-length = 120
4 changes: 4 additions & 0 deletions test/testLanguages.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
from lizard_languages import get_reader_for, CLikeReader, JavaReader, ObjCReader, JavaScriptReader, ScalaReader,\
GDScriptReader, SolidityReader, ErlangReader
from lizard_languages.zig import ZigReader


class TestLanguageChooser(unittest.TestCase):
Expand Down Expand Up @@ -36,3 +37,6 @@ def test_Solidity(self):

def test_erlang(self):
self.assertEqual(ErlangReader, get_reader_for("a.erl"))

def test_zig(self):
self.assertEqual(ZigReader, get_reader_for("a.zig"))
242 changes: 242 additions & 0 deletions test/test_languages/testZig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import unittest

from lizard import analyze_file


def get_zig_fileinfo(source_code):
return analyze_file.analyze_source_code("a.zig", source_code)


def get_zig_function_list(source_code):
return get_zig_fileinfo(source_code).function_list


class TestZig(unittest.TestCase):
def test_empty(self):
functions = get_zig_function_list("")
self.assertEqual(0, len(functions))

def test_one_function(self):
result = get_zig_function_list("""
pub fn sayGoodbye() void { }
""")
self.assertEqual(1, len(result))
self.assertEqual("sayGoodbye", result[0].name)
self.assertEqual(0, result[0].parameter_count)
self.assertEqual(1, result[0].cyclomatic_complexity)

def test_one_with_parameter(self):
result = get_zig_function_list("""
pub fn sayGoodbye(personName: string, alreadyGreeted: bool) void { }
""")
self.assertEqual(1, len(result))
self.assertEqual("sayGoodbye", result[0].name)
self.assertEqual(2, result[0].parameter_count)

def test_one_function_with_return_value(self):
result = get_zig_function_list("""
fn sayGoodbye() []const u8 { }
""")
self.assertEqual(1, len(result))
self.assertEqual("sayGoodbye", result[0].name)

def test_one_function_with_tuple_return_value(self):
result = get_zig_function_list("""
fn sayGoodbye(p: int) struct {[]const u8, bool} { }
""")
self.assertEqual(1, len(result))
self.assertEqual("sayGoodbye", result[0].name)
self.assertEqual(1, result[0].parameter_count)

def test_one_function_defined_on_a_struct(self):
result = get_zig_function_list("""
const Vec3 = struct {
x: f32,
y: f32,
z: f32,
pub fn dot(self: Vec3, other: Vec3) f32 {
return self.x * other.x + self.y * other.y + self.z * other.z;
}
};
""")
self.assertEqual(1, len(result))
self.assertEqual("dot", result[0].name)
self.assertEqual("dot self : Vec3 , other : Vec3", result[0].long_name)

def test_one_function_with_complexity(self):
result = get_zig_function_list("""
fn sayGoodbye() void { if ((diceRoll + 1) == 7) { diceRoll = 1 }}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

def test_nest_function(self):
result = get_zig_function_list("""
fn foo() fn() u32 {
const x: u32 = 123;
const T = struct {
fn bar() u32 { return x; }
fn baz(n: int) u32 { return x + 1; }
};
const T2 = struct {
fn bar() u32 {
return x;
}
};
return T.bar;
}
""")
self.assertEqual(4, len(result))

self.assertEqual("bar", result[0].name)
self.assertEqual("bar", result[0].long_name)
self.assertEqual(1, result[0].length)

self.assertEqual("baz", result[1].name)
self.assertEqual("baz n : int", result[1].long_name)
self.assertEqual(1, result[1].length)
self.assertEqual(["n : int"], result[1].full_parameters)

self.assertEqual("bar", result[2].name)
self.assertEqual("bar", result[2].long_name)
self.assertEqual(3, result[2].length)

self.assertEqual("foo", result[3].name)
self.assertEqual(16, result[3].length)

def test_struct(self):
result = get_zig_function_list("""
const Point = struct {
x: f32,
y: f32,
};
fn sayGoodbye() { }
""")
self.assertEqual(1, len(result))
self.assertEqual("sayGoodbye", result[0].name)

def test_interface_followed_by_enum_and_union(self):
result = get_zig_function_list("""
const Point = struct {
x: f32,
y: f32,
};
const Type = enum {
Ok,
NotOk,
};
const Payload = union {
Int: i64,
Float: f64,
Bool: bool,
};
""")
self.assertEqual(0, len(result))

def test_function_with_one(self):
result = get_zig_function_list("""
const std = @import("std");
pub fn main() !void {
std.debug.print("Hello, World!\n", .{});
}
""")
self.assertEqual(1, result[0].cyclomatic_complexity)

def test_function_with_two(self):
result = get_zig_function_list("""
const std = @import("std");
pub fn main() !void {
if (condition) {
std.debug.print("Hello, World!\n", .{});
}
}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

def test_function_with_three(self):
result = get_zig_function_list("""
const std = @import("std");
pub fn main() !void {
if (condition1 or condition2) {
std.debug.print("Hello, World!\n", .{});
}
}
""")
self.assertEqual(3, result[0].cyclomatic_complexity)

def test_function_with_eight(self):
result = get_zig_function_list("""
const std = @import("std");
pub fn main() !void {
switch (day) {
.Monday => std.debug.print("Today is Monday!\n", .{}),
.Tuesday => std.debug.print("Today is Tuesday!\n", .{}),
.Wednesday => std.debug.print("Today is Wednesday!\n", .{}),
.Thursday => std.debug.print("Today is Thursday!\n", .{}),
.Friday => std.debug.print("Today is Friday!\n", .{}),
.Saturday => std.debug.print("Today is Saturday!\n", .{}),
.Sunday => std.debug.print("Today is Sunday!\n", .{}),
}
}
""")
self.assertEqual(8, result[0].cyclomatic_complexity)

def test_null_orelse_operator(self):
result = get_zig_function_list("""
pub fn main() !void {
a orelse b;
}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

def test_try_operator(self):
result = get_zig_function_list("""
pub fn main(str: []u8) !void {
const number = try parseU64(str, 10);
_ = number;
}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

def test_catch_operator(self):
result = get_zig_function_list("""
pub fn main() !void {
const value: anyerror!u32 = error.Broken;
const unwrapped = value catch 1234;
unwrapped == 1234
}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

def test_for_loop(self):
result = get_zig_function_list("""
const std = @import("std");
pub fn main() !void {
for (0..5) {
std.debug.print("Hello, World!\n", .{});
}
}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

def test_while_loop(self):
result = get_zig_function_list("""
const std = @import("std");
pub fn main() !void {
var i: usize = 1;
while (i < 10) {
std.debug.print("Hello, World!\n", .{});
i *= 2;
}
}
""")
self.assertEqual(2, result[0].cyclomatic_complexity)

0 comments on commit a46563d

Please sign in to comment.