diff --git a/CMakeLists.txt b/CMakeLists.txt
index 802af85e..da567df5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,6 +21,7 @@ set(CMAKE_C_FLAGS_DEBUG "-g -DDEBUG=1")
include_directories(include extlib)
# build picrin
+include(contrib/CMakeLists.txt)
include(src/CMakeLists.txt)
include(tools/CMakeLists.txt)
diff --git a/README.md b/README.md
index 642f5084..8f9a5053 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,20 @@ Picrin is a lightweight scheme implementation intended to comply with full R7RS
Explicit renaming macro family.
+ - `(picrin regexp)`
+
+ - `(regexp? obj)`
+ - `(regexp ptrn [flags])`
+
+ Compiles pattern string into a regexp object. A string `flags` may contain any of #\g, #\i, #\m.
+
+ - `(regexp-match re input)`
+
+ Returns two values: a list of match strings, and a list of match indeces.
+
+ - `(regexp-replace re input txt)`
+ - `(regexp-split re input)`
+
- `(picrin user)`
When you start the REPL, you are dropped in here.
diff --git a/cmake/FindREGEX.cmake b/cmake/FindREGEX.cmake
new file mode 100644
index 00000000..bcae6f94
--- /dev/null
+++ b/cmake/FindREGEX.cmake
@@ -0,0 +1,64 @@
+# -*- cmake -*-
+#
+# FindRegex.cmake: Try to find Regex
+#
+# Copyright (C) 2005-2013 EDF-EADS-Phimeca
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# along with this library. If not, see .
+#
+# @author dutka
+# @date 2010-02-04 16:44:49 +0100 (Thu, 04 Feb 2010)
+#
+#
+# - Try to find Regex
+# Once done this will define
+#
+# REGEX_FOUND - System has Regex
+# REGEX_INCLUDE_DIR - The Regex include directory
+# REGEX_LIBRARIES - The libraries needed to use Regex
+# REGEX_DEFINITIONS - Compiler switches required for using Regex
+
+IF (REGEX_INCLUDE_DIR AND REGEX_LIBRARIES)
+ # in cache already
+ SET(Regex_FIND_QUIETLY TRUE)
+ENDIF (REGEX_INCLUDE_DIR AND REGEX_LIBRARIES)
+
+#IF (NOT WIN32)
+# # use pkg-config to get the directories and then use these values
+# # in the FIND_PATH() and FIND_LIBRARY() calls
+# FIND_PACKAGE(PkgConfig)
+# PKG_CHECK_MODULES(PC_REGEX regex)
+# SET(REGEX_DEFINITIONS ${PC_REGEX_CFLAGS_OTHER})
+#ENDIF (NOT WIN32)
+
+FIND_PATH(REGEX_INCLUDE_DIR regex.h
+ HINTS
+ ${REGEX_INCLUDEDIR}
+ ${PC_LIBXML_INCLUDE_DIRS}
+ PATH_SUFFIXES regex
+ )
+
+FIND_LIBRARY(REGEX_LIBRARIES NAMES c regex
+ HINTS
+ ${PC_REGEX_LIBDIR}
+ ${PC_REGEX_LIBRARY_DIRS}
+ )
+
+INCLUDE(FindPackageHandleStandardArgs)
+
+# handle the QUIETLY and REQUIRED arguments and set REGEX_FOUND to TRUE if
+# all listed variables are TRUE
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(Regex DEFAULT_MSG REGEX_LIBRARIES REGEX_INCLUDE_DIR)
+
+MARK_AS_ADVANCED(REGEX_INCLUDE_DIR REGEX_LIBRARIES)
diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt
new file mode 100644
index 00000000..f206ac80
--- /dev/null
+++ b/contrib/CMakeLists.txt
@@ -0,0 +1,3 @@
+include(contrib/regexp/CMakeLists.txt)
+
+add_definitions("-DPIC_CONTRIB_INITS=${PICRIN_CONTRIB_INITS}")
diff --git a/contrib/regexp/CMakeLists.txt b/contrib/regexp/CMakeLists.txt
new file mode 100644
index 00000000..0e28d430
--- /dev/null
+++ b/contrib/regexp/CMakeLists.txt
@@ -0,0 +1,13 @@
+# regex
+find_package(REGEX)
+
+if (REGEX_FOUND)
+ add_definitions(${REGEX_DEFINITIONS})
+ include_directories(${REGEX_INCLUDE_DIR})
+
+ file(GLOB PICRIN_REGEX_SOURCES ${PROJECT_SOURCE_DIR}/contrib/regexp/src/*.c)
+
+ list(APPEND PICRIN_CONTRIB_INITS "void pic_init_regexp(pic_state *)\; pic_init_regexp(pic)\;")
+ list(APPEND PICRIN_CONTRIB_LIBRARIES ${REGEX_LIBRARIES})
+ list(APPEND PICRIN_CONTRIB_SOURCES ${PICRIN_REGEX_SOURCES})
+endif()
diff --git a/contrib/regexp/src/regexp.c b/contrib/regexp/src/regexp.c
new file mode 100644
index 00000000..5ee5d477
--- /dev/null
+++ b/contrib/regexp/src/regexp.c
@@ -0,0 +1,193 @@
+#include "picrin.h"
+#include "picrin/data.h"
+#include "picrin/pair.h"
+#include "picrin/string.h"
+#include "picrin/cont.h"
+
+#include
+
+struct pic_regexp_t {
+ regex_t reg;
+ const char *flags;
+};
+
+static void
+regexp_dtor(pic_state *pic, void *data)
+{
+ struct pic_regexp_t *preg;
+
+ preg = data;
+ regfree(&preg->reg);
+ pic_free(pic, data);
+}
+
+static const pic_data_type regexp_type = { "regexp", regexp_dtor };
+
+#define pic_regexp_p(o) (pic_data_type_p((o), ®exp_type))
+#define pic_regexp_data_ptr(o) ((struct pic_regexp_t *)pic_data_ptr(o)->data)
+
+static pic_value
+pic_regexp_regexp(pic_state *pic)
+{
+ const char *ptrn, *flags = "";
+ int cflags, err;
+ struct pic_regexp_t *reg;
+
+ pic_get_args(pic, "z|z", &ptrn, &flags);
+
+ cflags = REG_EXTENDED;
+
+ while (*flags) {
+ switch (*flags++) {
+ case 'g':
+ case 'G':
+ /* pass */
+ break;
+ case 'i':
+ case 'I':
+ cflags |= REG_ICASE;
+ break;
+ case 'm':
+ case 'M':
+ cflags |= REG_NEWLINE;
+ break;
+ }
+ }
+
+ reg = pic_alloc(pic, sizeof(struct pic_regexp_t));
+ reg->flags = flags;
+
+ if ((err = regcomp(®->reg, ptrn, cflags)) != 0) {
+ char errbuf[regerror(err, ®->reg, NULL, 0)];
+
+ regerror(err, ®->reg, errbuf, sizeof errbuf);
+ regexp_dtor(pic, ®->reg);
+
+ pic_errorf(pic, "regexp compilation error: %s", errbuf);
+ }
+
+ return pic_obj_value(pic_data_alloc(pic, ®exp_type, reg));
+}
+
+static pic_value
+pic_regexp_regexp_p(pic_state *pic)
+{
+ pic_value obj;
+
+ pic_get_args(pic, "o", &obj);
+
+ return pic_bool_value(pic_regexp_p(obj));
+}
+
+static pic_value
+pic_regexp_regexp_match(pic_state *pic)
+{
+ pic_value reg;
+ const char *input;
+ regmatch_t match[100];
+ pic_value matches, positions;
+ pic_str *str;
+ int i, offset;
+
+ pic_get_args(pic, "oz", ®, &input);
+
+ pic_assert_type(pic, reg, regexp);
+
+ matches = pic_nil_value();
+ positions = pic_nil_value();
+
+ if (strchr(pic_regexp_data_ptr(reg)->flags, 'g') != NULL) {
+ /* global search */
+
+ offset = 0;
+ while (regexec(&pic_regexp_data_ptr(reg)->reg, input, 1, match, 0) != REG_NOMATCH) {
+ pic_push(pic, pic_obj_value(pic_str_new(pic, input, match[0].rm_eo - match[0].rm_so)), matches);
+ pic_push(pic, pic_int_value(offset), positions);
+
+ offset += match[0].rm_eo;
+ input += match[0].rm_eo;
+ }
+ } else {
+ /* local search */
+
+ if (regexec(&pic_regexp_data_ptr(reg)->reg, input, 100, match, 0) == 0) {
+ for (i = 0; i < 100; ++i) {
+ if (match[i].rm_so == -1) {
+ break;
+ }
+ str = pic_str_new(pic, input + match[i].rm_so, match[i].rm_eo - match[i].rm_so);
+ pic_push(pic, pic_obj_value(str), matches);
+ pic_push(pic, pic_int_value(match[i].rm_so), positions);
+ }
+ }
+ }
+
+ if (pic_nil_p(matches)) {
+ matches = pic_false_value();
+ positions = pic_false_value();
+ } else {
+ matches = pic_reverse(pic, matches);
+ positions = pic_reverse(pic, positions);
+ }
+ return pic_values2(pic, matches, positions);
+}
+
+static pic_value
+pic_regexp_regexp_split(pic_state *pic)
+{
+ pic_value reg;
+ const char *input;
+ regmatch_t match;
+ pic_value output = pic_nil_value();
+
+ pic_get_args(pic, "oz", ®, &input);
+
+ pic_assert_type(pic, reg, regexp);
+
+ while (regexec(&pic_regexp_data_ptr(reg)->reg, input, 1, &match, 0) != REG_NOMATCH) {
+ pic_push(pic, pic_obj_value(pic_str_new(pic, input, match.rm_so)), output);
+
+ input += match.rm_eo;
+ }
+
+ pic_push(pic, pic_obj_value(pic_str_new_cstr(pic, input)), output);
+
+ return pic_reverse(pic, output);
+}
+
+static pic_value
+pic_regexp_regexp_replace(pic_state *pic)
+{
+ pic_value reg;
+ const char *input;
+ regmatch_t match;
+ pic_str *txt, *output = pic_str_new(pic, NULL, 0);
+
+ pic_get_args(pic, "ozs", ®, &input, &txt);
+
+ pic_assert_type(pic, reg, regexp);
+
+ while (regexec(&pic_regexp_data_ptr(reg)->reg, input, 1, &match, 0) != REG_NOMATCH) {
+ output = pic_strcat(pic, output, pic_str_new(pic, input, match.rm_so));
+ output = pic_strcat(pic, output, txt);
+
+ input += match.rm_eo;
+ }
+
+ output = pic_strcat(pic, output, pic_str_new(pic, input, strlen(input)));
+
+ return pic_obj_value(output);
+}
+
+void
+pic_init_regexp(pic_state *pic)
+{
+ pic_deflibrary ("(picrin regexp)") {
+ pic_defun(pic, "regexp", pic_regexp_regexp);
+ pic_defun(pic, "regexp?", pic_regexp_regexp_p);
+ pic_defun(pic, "regexp-match", pic_regexp_regexp_match);
+ /* pic_defun(pic, "regexp-search", pic_regexp_regexp_search); */
+ pic_defun(pic, "regexp-split", pic_regexp_regexp_split);
+ pic_defun(pic, "regexp-replace", pic_regexp_regexp_replace);
+ }
+}
diff --git a/include/config.h b/include/config.h
index b289d9f6..2d5fd2a2 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2,6 +2,9 @@
* See Copyright Notice in picrin.h
*/
+/** contribution libraries */
+/* #define PIC_CONTRIB_INITS */
+
/** switch normal VM and direct threaded VM */
/* #define PIC_DIRECT_THREADED_VM 1 */
@@ -42,6 +45,10 @@
/* #define GC_DEBUG 1 */
/* #define GC_DEBUG_DETAIL 1 */
+#ifndef PIC_CONTRIB_INITS
+# define PIC_CONTRIB_INITS
+#endif
+
#ifndef PIC_DIRECT_THREADED_VM
# if defined(__GNUC__) || defined(__CLANG__)
# define PIC_DIRECT_THREADED_VM 1
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6a9705c0..93af4e22 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -7,6 +7,6 @@ set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${PROJECT_SOURCE
set(XFILE_SOURCES extlib/xfile/xfile.c)
# build!
-file(GLOB C_SOURCES ${PROJECT_SOURCE_DIR}/src/*.c)
-add_library(picrin SHARED ${C_SOURCES} ${FLEX_scan_OUTPUTS} ${XFILE_SOURCES})
-target_link_libraries(picrin m)
+file(GLOB PICRIN_SOURCES ${PROJECT_SOURCE_DIR}/src/*.c)
+add_library(picrin SHARED ${PICRIN_SOURCES} ${FLEX_scan_OUTPUTS} ${XFILE_SOURCES} ${PICRIN_CONTRIB_SOURCES})
+target_link_libraries(picrin m ${PICRIN_CONTRIB_LIBRARIES})
diff --git a/src/init.c b/src/init.c
index 537dbdf6..57a48c55 100644
--- a/src/init.c
+++ b/src/init.c
@@ -51,6 +51,12 @@ pic_load_stdlib(pic_state *pic)
}
+void
+pic_init_contrib(pic_state *pic)
+{
+ PIC_CONTRIB_INITS
+}
+
#define PUSH_SYM(pic, lst, name) \
lst = pic_cons(pic, pic_symbol_value(pic_intern_cstr(pic, name)), lst)
@@ -109,6 +115,8 @@ pic_init_core(pic_state *pic)
pic_load_stdlib(pic); DONE;
+ pic_init_contrib(pic); DONE;
+
pic_defun(pic, "features", pic_features);
}