diff --git a/include/rjp_internal.h b/include/rjp_internal.h
index a2d52a7..7fc4ff8 100644
--- a/include/rjp_internal.h
+++ b/include/rjp_internal.h
@@ -1,3 +1,21 @@
+/**
+ rjp
+ Copyright (C) 2018 rexy712
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
#ifndef RJP_INTERNAL_H
#define RJP_INTERNAL_H
diff --git a/src/input.c b/src/input.c
index 230d96d..1ccda5e 100644
--- a/src/input.c
+++ b/src/input.c
@@ -17,7 +17,6 @@
*/
//TODO: Scientific notation
-//TODO: \e escape sequence in strings
#include "rjp.h"
#include "rjp_internal.h"
diff --git a/src/output.c b/src/output.c
index facdf51..c7050b4 100644
--- a/src/output.c
+++ b/src/output.c
@@ -1,3 +1,21 @@
+/**
+ rjp
+ Copyright (C) 2018 rexy712
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
#include "rjp.h"
#include "rjp_internal.h"
diff --git a/src/strings.c b/src/strings.c
index a0e8505..eb48d19 100644
--- a/src/strings.c
+++ b/src/strings.c
@@ -1,32 +1,178 @@
+/**
+ rjp
+ Copyright (C) 2018 rexy712
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
#include "rjp.h"
#include "rjp_internal.h"
#include //fprintf
#include //malloc, free
+#include
+
+static uint32_t utf_strtol_4(const char* c){
+ uint32_t ret = 0;
+ for(size_t i = 0;i < 4;++i){
+ if(c[i] >= '0' && c[i] <= '9'){
+ ret |= ((c[i] ^ 0x30) << (4*(3-i)));
+ }else if(c[i] >= 'A' && c[i] <= 'F'){
+ ret |= ((c[i] - 0x37) << (4*(3-i)));
+ }else if(c[i] >= 'a' && c[i] <= 'f'){
+ ret |= ((c[i] - 0x57) << (4*(3-i)));
+ }else{
+ return 0;
+ }
+ }
+ return ret;
+}
+
+
+static int decode_unicode_escape(const char* str, uint32_t* high, uint32_t* low){
+ if(*str != '\\' || *(str+1) != 'u'){ //invalid
+ return *low = *high = 0;
+ }
+ *high = utf_strtol_4(str+2);
+ if(!*high)
+ return *low = *high = 0;
+ if((*high & 0xF800) == 0xD800){ //utf-16
+ if(*(str+6) != '\\' || *(str+7) != 'u'){
+ return *low = *high = 0;
+ }
+ *low = utf_strtol_4(str+8);
+ return 12;
+ }else{
+ *low = 0;
+ }
+ return 6;
+}
+
+static uint32_t u16_surrogate_pair_to_codepoint(uint32_t high, uint32_t low){
+ uint32_t codepoint;
+
+ codepoint = ((high & 0x07FF) << 10) | (1 << 16);
+ codepoint = codepoint | (low & 0x03FF);
+
+ return codepoint;
+}
+
+static uint32_t utf_to_codepoint(uint32_t high, uint32_t low){
+ if(!low) //utf8
+ return high;
+ return u16_surrogate_pair_to_codepoint(high, low);
+}
+static int codepoint_strlen(uint32_t codepoint){
+ if(codepoint <= 0x007F){
+ return 1;
+ }else if(codepoint <= 0x07FF){
+ return 2;
+ }else if(codepoint <= 0xFFFF){
+ return 3;
+ }else if(codepoint <= 0x10FFFF){
+ return 4;
+ }else{
+ return 0;
+ }
+}
+
+static int codepoint_to_u8(char* dest, uint32_t codepoint){
+ if(codepoint <= 0x007F){
+ dest[0] = codepoint;
+ return 1;
+ }else if(codepoint <= 0x07FF){
+ dest[0] = (codepoint >> 6) | 0xC0;
+ dest[1] = (codepoint & 0x3F) | 0x80;
+ return 2;
+ }else if(codepoint <= 0xFFFF){
+ dest[0] = (codepoint >> 12) | 0xE0;
+ dest[1] = ((codepoint >> 6) & 0x3F) | 0x80;
+ dest[2] = (codepoint & 0x3F) | 0x80;
+ return 3;
+ }else if(codepoint <= 0x10FFFF){
+ dest[0] = (codepoint >> 18) | 0xF0;
+ dest[1] = ((codepoint >> 12) & 0x3F) | 0x80;
+ dest[2] = ((codepoint >> 6) & 0x3F) | 0x80;
+ dest[3] = (codepoint & 0x3F) | 0x80;
+ return 4;
+ }else{
+ return 0;
+ }
+}
+static uint32_t u8_to_codepoint(char* u){
+ if((u[0] & 0x80) == 0){
+ //one byte
+ return u[0];
+ }else if((u[0] & 0xE0) == 0xC0){
+ //two byte
+ uint32_t codepoint;
+ codepoint = (u[0] & 0x1F) << 6;
+ codepoint |= (u[1] & 0x3F);
+ return codepoint;
+ }else if((u[0] & 0xF0) == 0xE0){
+ //three byte
+ uint32_t codepoint;
+ codepoint = (u[0] & 0x0F) << 12;
+ codepoint |= (u[1] & 0x3F) << 6;
+ codepoint |= (u[2] & 0x3F);
+ return codepoint;
+ }else if((u[0] & 0xF8) == 0xF0){
+ //four byte
+ uint32_t codepoint;
+ codepoint = (u[0] & 0x07) << 18;
+ codepoint |= (u[1] & 0x3F) << 12;
+ codepoint |= (u[2] & 0x3F) << 6;
+ codepoint |= (u[3] & 0x3F);
+ return codepoint;
+ }else{
+ //invalid
+ return 0;
+ }
+}
//Convert escape sequences in strings
char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, int* column){
char* new_string;
++(*column); //account for starting quotation mark
- for(*len = 0;*(str+*len) != '"';++(*len), ++(*column)){
- if(*(str+*len) == '\\'){
- ++(*len);
- ++(*column);
- }else if(*(str+*len) == '\0'){
- *len = 1;
+ int oldpos = 0;
+ int newpos = 0;
+ for(;*(str+oldpos) != '"';++oldpos, ++(newpos), ++(*column)){
+ if(*(str+oldpos) == '\\'){
+ if(*(str+oldpos+1) == 'u'){
+ uint32_t high, low;
+ oldpos += (decode_unicode_escape(str+oldpos, &high, &low)-1);
+ newpos += (codepoint_strlen(utf_to_codepoint(high, low))-1);
+ }else{
+ ++oldpos;
+ ++(*column);
+ }
+ }else if(*(str+oldpos) == '\0'){
+ newpos = 1;
fprintf(stderr, "Syntax error! %s (%i:%i)\n", "Unexpected EOF in string!", *row, *column);
rjp_free_value(root);
return NULL;
- }else if(*(str+*len) == '\n'){
+ }else if(*(str+oldpos) == '\n'){
++(*row);
*column = 0;
}
}
- if(*len == 0){
+ *len = oldpos;
+ if(newpos == 0){
return NULL;
}
- new_string = rjp_alloc(*len + 1);
- new_string[*len] = 0;
+ new_string = rjp_alloc(newpos + 1);
+ new_string[newpos] = 0;
for(int i = 0;*str != '"';++i,++str){
if(*str == '\\'){
++str;
@@ -52,6 +198,18 @@ char* _rjp__parse_string(RJP_value* root, const char* str, int* len, int* row, i
case 'f':
new_string[i] = '\f';
break;
+ case 'u':;
+ uint32_t high, low;
+ uint32_t codepoint;
+ --str;
+ str += (decode_unicode_escape(str, &high, &low) - 1);
+ if(!high){
+ rjp_free(new_string);
+ return NULL;
+ }
+ codepoint = utf_to_codepoint(high, low);
+ i += (codepoint_to_u8(new_string+i, codepoint)-1);
+ break;
default:
new_string[i] = *str;
break;