mirror of
				https://github.com/janet-lang/janet
				synced 2025-11-04 01:23:04 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			193 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Janet
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Janet
		
	
	
	
	
	
# Copyright (c) 2023 Calvin Rose
 | 
						|
#
 | 
						|
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
						|
# of this software and associated documentation files (the "Software"), to
 | 
						|
# deal in the Software without restriction, including without limitation the
 | 
						|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | 
						|
# sell copies of the Software, and to permit persons to whom the Software is
 | 
						|
# furnished to do so, subject to the following conditions:
 | 
						|
#
 | 
						|
# The above copyright notice and this permission notice shall be included in
 | 
						|
# all copies or substantial portions of the Software.
 | 
						|
#
 | 
						|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | 
						|
# IN THE SOFTWARE.
 | 
						|
 | 
						|
(import ./helper :prefix "" :exit true)
 | 
						|
(start-suite)
 | 
						|
 | 
						|
# 7e46ead2f
 | 
						|
(assert (not false) "false literal")
 | 
						|
(assert true "true literal")
 | 
						|
(assert (not nil) "nil literal")
 | 
						|
 | 
						|
(assert (= '(1 2 3) (quote (1 2 3)) (tuple 1 2 3)) "quote shorthand")
 | 
						|
 | 
						|
# String literals
 | 
						|
# 45f8db0
 | 
						|
(assert (= "abcd" "\x61\x62\x63\x64") "hex escapes")
 | 
						|
(assert (= "\e" "\x1B") "escape character")
 | 
						|
(assert (= "\x09" "\t") "tab character")
 | 
						|
 | 
						|
# Long strings
 | 
						|
# 7e6342720
 | 
						|
(assert (= "hello, world" `hello, world`) "simple long string")
 | 
						|
(assert (= "hello, \"world\"" `hello, "world"`)
 | 
						|
        "long string with embedded quotes")
 | 
						|
(assert (= "hello, \\\\\\ \"world\"" `hello, \\\ "world"`)
 | 
						|
        "long string with embedded quotes and backslashes")
 | 
						|
 | 
						|
#
 | 
						|
# Longstring indentation
 | 
						|
#
 | 
						|
# 7aa4241
 | 
						|
(defn reindent
 | 
						|
  "Reindent the contents of a longstring as the Janet parser would.
 | 
						|
  This include removing leading and trailing newlines."
 | 
						|
  [text indent]
 | 
						|
 | 
						|
  # Detect minimum indent
 | 
						|
  (var rewrite true)
 | 
						|
  (each index (string/find-all "\n" text)
 | 
						|
    (for i (+ index 1) (+ index indent 1)
 | 
						|
      (case (get text i)
 | 
						|
        nil (break)
 | 
						|
        (chr "\n") (break)
 | 
						|
        (chr " ") nil
 | 
						|
        (set rewrite false))))
 | 
						|
 | 
						|
  # Only re-indent if no dedented characters.
 | 
						|
  (def str
 | 
						|
    (if rewrite
 | 
						|
      (peg/replace-all ~(* "\n" (between 0 ,indent " ")) "\n" text)
 | 
						|
      text))
 | 
						|
 | 
						|
  (def first-nl (= (chr "\n") (first str)))
 | 
						|
  (def last-nl (= (chr "\n") (last str)))
 | 
						|
  (string/slice str (if first-nl 1 0) (if last-nl -2)))
 | 
						|
 | 
						|
(defn reindent-reference
 | 
						|
  "Same as reindent but use parser functionality. Useful for
 | 
						|
  validating conformance."
 | 
						|
  [text indent]
 | 
						|
  (if (empty? text) (break text))
 | 
						|
  (def source-code
 | 
						|
    (string (string/repeat " " indent) "``````"
 | 
						|
            text
 | 
						|
            "``````"))
 | 
						|
  (parse source-code))
 | 
						|
 | 
						|
(var indent-counter 0)
 | 
						|
(defn check-indent
 | 
						|
  [text indent]
 | 
						|
  (++ indent-counter)
 | 
						|
  (let [a (reindent text indent)
 | 
						|
        b (reindent-reference text indent)]
 | 
						|
    (assert (= a b)
 | 
						|
            (string "indent " indent-counter " (indent=" indent ")"))))
 | 
						|
 | 
						|
(check-indent "" 0)
 | 
						|
(check-indent "\n" 0)
 | 
						|
(check-indent "\n" 1)
 | 
						|
(check-indent "\n\n" 0)
 | 
						|
(check-indent "\n\n" 1)
 | 
						|
(check-indent "\nHello, world!" 0)
 | 
						|
(check-indent "\nHello, world!" 1)
 | 
						|
(check-indent "Hello, world!" 0)
 | 
						|
(check-indent "Hello, world!" 1)
 | 
						|
(check-indent "\n    Hello, world!" 4)
 | 
						|
(check-indent "\n    Hello, world!\n" 4)
 | 
						|
(check-indent "\n    Hello, world!\n   " 4)
 | 
						|
(check-indent "\n    Hello, world!\n    " 4)
 | 
						|
(check-indent "\n    Hello, world!\n   dedented text\n    " 4)
 | 
						|
(check-indent "\n    Hello, world!\n    indented text\n    " 4)
 | 
						|
 | 
						|
# Symbols with @ character
 | 
						|
# d68eae9
 | 
						|
(def @ 1)
 | 
						|
(assert (= @ 1) "@ symbol")
 | 
						|
(def @-- 2)
 | 
						|
(assert (= @-- 2) "@-- symbol")
 | 
						|
(def @hey 3)
 | 
						|
(assert (= @hey 3) "@hey symbol")
 | 
						|
 | 
						|
# Parser clone
 | 
						|
# 43520ac67
 | 
						|
(def p (parser/new))
 | 
						|
(assert (= 7 (parser/consume p "(1 2 3 ")) "parser 1")
 | 
						|
(def p2 (parser/clone p))
 | 
						|
(parser/consume p2 ") 1 ")
 | 
						|
(parser/consume p ") 1 ")
 | 
						|
(assert (deep= (parser/status p) (parser/status p2)) "parser 2")
 | 
						|
(assert (deep= (parser/state p) (parser/state p2)) "parser 3")
 | 
						|
 | 
						|
# Parser errors
 | 
						|
# 976dfc719
 | 
						|
(defn parse-error [input]
 | 
						|
  (def p (parser/new))
 | 
						|
  (parser/consume p input)
 | 
						|
  (parser/error p))
 | 
						|
 | 
						|
# Invalid utf-8 sequences
 | 
						|
(assert (not= nil (parse-error @"\xc3\x28")) "reject invalid utf-8 symbol")
 | 
						|
(assert (not= nil (parse-error @":\xc3\x28")) "reject invalid utf-8 keyword")
 | 
						|
 | 
						|
# Parser line and column numbers
 | 
						|
# 77b79e989
 | 
						|
(defn parser-location [input &opt location]
 | 
						|
  (def p (parser/new))
 | 
						|
  (parser/consume p input)
 | 
						|
  (if location
 | 
						|
    (parser/where p ;location)
 | 
						|
    (parser/where p)))
 | 
						|
 | 
						|
(assert (= [1 7] (parser-location @"(+ 1 2)")) "parser location 1")
 | 
						|
(assert (= [5 7] (parser-location @"(+ 1 2)" [5])) "parser location 2")
 | 
						|
(assert (= [10 10] (parser-location @"(+ 1 2)" [10 10])) "parser location 3")
 | 
						|
 | 
						|
# Issue #861 - should be valgrind clean
 | 
						|
# 39c6be7cb
 | 
						|
(def step1 "(a b c d)\n")
 | 
						|
(def step2 "(a b)\n")
 | 
						|
(def p1 (parser/new))
 | 
						|
(parser/state p1)
 | 
						|
(parser/consume p1 step1)
 | 
						|
(loop [v :iterate (parser/produce p1)])
 | 
						|
(parser/state p1)
 | 
						|
(def p2 (parser/clone p1))
 | 
						|
(parser/state p2)
 | 
						|
(parser/consume p2 step2)
 | 
						|
(loop [v :iterate (parser/produce p2)])
 | 
						|
(parser/state p2)
 | 
						|
 | 
						|
# parser delimiter errors
 | 
						|
(defn test-error [delim fmt]
 | 
						|
  (def p (parser/new))
 | 
						|
  (parser/consume p delim)
 | 
						|
  (parser/eof p)
 | 
						|
  (def msg (string/format fmt delim))
 | 
						|
  (assert (= (parser/error p) msg) "delimiter error"))
 | 
						|
(each c [ "(" "{" "[" "\"" "``" ]
 | 
						|
  (test-error c "unexpected end of source, %s opened at line 1, column 1"))
 | 
						|
 | 
						|
# parser/insert
 | 
						|
(def p (parser/new))
 | 
						|
(parser/consume p "(")
 | 
						|
(parser/insert p "hello")
 | 
						|
(parser/consume p ")")
 | 
						|
(assert (= (parser/produce p) ["hello"]))
 | 
						|
 | 
						|
(def p (parser/new))
 | 
						|
(parser/consume p `("hel`)
 | 
						|
(parser/insert p `lo`)
 | 
						|
(parser/consume p `")`)
 | 
						|
(assert (= (parser/produce p) ["hello"]))
 | 
						|
 | 
						|
(end-suite)
 | 
						|
 |