From 427327003d639c943fd770734b13db9bb14fa8d6 Mon Sep 17 00:00:00 2001 From: Pekka Laiho Date: Tue, 16 Jun 2020 20:38:46 +0700 Subject: [PATCH] handle special characters correctly in printing --- README.md | 2 +- run.php | 6 +++--- src/Lisp.php | 10 +++++----- src/Printer.php | 22 +++++++++++++++------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 0fb0c54..51ba2e2 100644 --- a/README.md +++ b/README.md @@ -135,7 +135,7 @@ Name | Example | Example result | Description ----- | ------- | -------------- | ----------- doc | `(doc +)` | `"Return the sum of all arguments."` | Show description of a built-in function. read | `(read "(+ 1 2 3)")` | `(+ 1 2 3)` | Read a string as code and return the expression. -print | `(print "hello world")` | `"hello world"null` | Print expression on the screen. Print returns null (which is shown due to the extra print in repl). +print | `(print "hello world")` | `"hello world"null` | Print expression on the screen. Print returns null (which is shown due to the extra print in repl). Give optional second argument as `true` to show strings in readable format. error | `(error "invalid value")` | `error: invalid value` | Throw an exception with message as argument. ### Collection functions diff --git a/run.php b/run.php index 1b27934..e2c8fe8 100644 --- a/run.php +++ b/run.php @@ -17,7 +17,7 @@ function ml_repl($lisp, $env) $input = readline('> '); try { - $lisp->rep($input, $env); + $lisp->rep($input, $env, true); if ($input) { readline_add_history($input); @@ -42,10 +42,10 @@ function ml_run() list($lisp, $env) = ml_get_lisp($debug); if (array_key_exists('e', $args)) { - $lisp->rep($args['e'], $env); + $lisp->rep($args['e'], $env, false); } elseif (array_key_exists('f', $args)) { $input = "(load \"{$args['f']}\")"; - $lisp->rep($input, $env); + $lisp->rep($input, $env, false); } elseif (array_key_exists('r', $args)) { ml_repl($lisp, $env); } else { diff --git a/src/Lisp.php b/src/Lisp.php index 163b27f..6ede8ed 100644 --- a/src/Lisp.php +++ b/src/Lisp.php @@ -16,7 +16,7 @@ class Lisp $this->printer = $printer; } - public function rep(string $input, Env $env): void + public function rep(string $input, Env $env, bool $printReadable): void { $tokens = $this->tokenizer->tokenize($input); @@ -24,7 +24,7 @@ class Lisp $result = $this->eval->eval($expr, $env); - $this->printer->print($result); + $this->printer->print($result, $printReadable); } public function register(Env $env): void @@ -43,9 +43,9 @@ class Lisp fn (string $a) => $this->reader->read($this->tokenizer->tokenize($a)) )); - $env->set('print', new CoreFunc('print', 'Print argument.', 1, 1, - function ($a) { - $this->printer->print($a); + $env->set('print', new CoreFunc('print', 'Print argument. Give second argument as true to show strings in readable format.', 1, 2, + function ($a, bool $readable = false) { + $this->printer->print($a, $readable); return null; } )); diff --git a/src/Printer.php b/src/Printer.php index e1b4d95..34099bc 100644 --- a/src/Printer.php +++ b/src/Printer.php @@ -3,21 +3,21 @@ namespace MadLisp; class Printer { - public function print($ast): void + public function print($ast, bool $readable = true): void { - print($this->doPrint($ast)); + print($this->doPrint($ast, $readable)); } - private function doPrint($a): string + private function doPrint($a, bool $readable): string { if ($a instanceof Func) { return ''; } elseif ($a instanceof MList) { - return '(' . implode(' ', array_map(fn ($b) => $this->doPrint($b), $a->getData())) . ')'; + return '(' . implode(' ', array_map(fn ($b) => $this->doPrint($b, $readable), $a->getData())) . ')'; } elseif ($a instanceof Vector) { - return '[' . implode(' ', array_map(fn ($b) => $this->doPrint($b), $a->getData())) . ']'; + return '[' . implode(' ', array_map(fn ($b) => $this->doPrint($b, $readable), $a->getData())) . ']'; } elseif ($a instanceof Hash) { - return '{' . implode(' ', array_map(fn ($key, $val) => $this->doPrint($key) . ':' . $this->doPrint($val), + return '{' . implode(' ', array_map(fn ($key, $val) => $this->doPrint($key, $readable) . ':' . $this->doPrint($val, $readable), array_keys($a->getData()), array_values($a->getData()))) . '}'; } elseif ($a instanceof Symbol) { return $a->getName(); @@ -28,7 +28,15 @@ class Printer } elseif ($a === null) { return 'null'; } elseif (is_string($a)) { - return '"' . $a . '"'; + if ($readable) { + $a = str_replace("\\", "\\\\", $a); + $a = str_replace("\n", "\\n", $a); + $a = str_replace("\r", "\\r", $a); + $a = str_replace("\"", "\\\"", $a); + return '"' . $a . '"'; + } else { + return $a; + } } else { return $a; }