Skip to content

Commit 8aa9755

Browse files
committed
NPScript 0.23.0
- (Fix) Symbols were being resolved from the oldest dict to the newest dict instead of the other way around - 'symbols' has been replaced with 'psymbols', 'symbols' now operates differently - 'langver' has been renamed to 'version' - Added the 'product', 'bind', 'rsymbols', 'psymbols', and 'prsymbols' builtin operators - 'get' and 'put' now support negative indexes - Procedures can now be 'literal', meaning symbols inside of them are pre-resolved
1 parent 74de376 commit 8aa9755

11 files changed

Lines changed: 320 additions & 118 deletions

File tree

npscript.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

3-
version = "0.22.0"
3+
version = "0.23.0"
44
author = "Nuclear Pasta"
55
description = "A PostScript implementation"
66
license = "Apache-2.0"

src/builtinlibs/libstrings.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ Combines a list of strings L into a single string S, separated by delimiter D.
7272
var strs = newSeqOfCap[string](l.len)
7373

7474
for v in l:
75-
if v.kind != tString:
75+
if v.typ != tString:
7676
raise newNpsError("List argument for 'joins' must be only strings")
7777

7878
strs.add(v.strv)

src/builtins.nim

Lines changed: 149 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ import std/[
66
random,
77
os,
88
sequtils,
9-
math
9+
math,
10+
enumerate
1011
]
1112

1213
from system/nimscript import nil
1314

1415
import
1516
npsenv,
1617
lexer,
18+
parser,
1719
builtinlibs/[
1820
common,
1921
libstrings,
@@ -27,15 +29,28 @@ elif defined(danger):
2729
else:
2830
const buildMode = "debug"
2931

30-
const langVersion* = staticRead("../npscript.nimble")
31-
.split("\n")
32-
.filterIt(it.startsWith("version"))[0]
33-
.split("=")[^1]
34-
.strip()[1..^2] & (if buildMode != "release": "+" & buildMode else: "")
32+
const
33+
langVersion* = staticRead("../npscript.nimble")
34+
.split("\n")
35+
.filterIt(it.startsWith("version"))[0]
36+
.split("=")[^1]
37+
.strip()[1..^2] & (if buildMode != "release": "+" & buildMode else: "")
38+
39+
product = "NPScript"
40+
41+
helpMessage = staticRead("data/helpmsg.txt")
42+
.strip()
43+
.colorize()
44+
45+
helpMessageExtended = staticRead("data/helpmsgext.txt")
46+
.strip()
47+
.colorize()
48+
3549

3650
static:
3751
echo "Compiling NPScript ", langVersion, " on ", nimscript.buildOS, "/", nimscript.buildCPU, " for ", hostOS, "/", hostCPU, " in ", buildMode, " mode"
3852

53+
3954
let builtins* = newDict(0)
4055

4156
template addV(name, doc: static string, item: Value) =
@@ -129,24 +144,47 @@ proc importFile*(s: State, path: string): Value =
129144
return substate.get("export")
130145

131146

147+
proc literalize(s: State, nodes: seq[Node]): seq[Value] =
148+
result = newSeq[Value](nodes.len)
149+
150+
for (i, node) in enumerate(nodes):
151+
case node.typ
152+
of nSymbol:
153+
result[i] = newSymbol(node.tok.lit)
154+
of nString:
155+
result[i] = newString(node.tok.lit)
156+
of nInteger:
157+
let num = parseInt(node.tok.lit)
158+
result[i] = newInteger(num)
159+
of nReal:
160+
let num = parseFloat(node.tok.lit)
161+
result[i] = newReal(num)
162+
of nList:
163+
result[i] = newList(literalize(s, node.nodes))
164+
of nProc:
165+
result[i] = newProcedure(node.nodes)
166+
of nWord:
167+
result[i] = s.get(node.tok.lit)
168+
169+
132170
# Meta operators
133171

134-
addV("langver",
172+
addV("version",
135173
"""
136-
'langver'
137-
-> version
174+
'version'
175+
-> string
138176
Returns the current version of the language as a string.
139177
"""):
140178
newString(langVersion)
141179

142-
const
143-
helpMessage = staticRead("data/helpmsg.txt")
144-
.strip()
145-
.colorize()
146-
147-
helpMessageExtended = staticRead("data/helpmsgext.txt")
148-
.strip()
149-
.colorize()
180+
addV("product",
181+
"""
182+
'product'
183+
-> string
184+
Returns the product name.
185+
This is included for compatibility with other PostScript implementations.
186+
"""):
187+
newString(product)
150188

151189
# ->
152190
# Prints a help message to assist people with writing the language.
@@ -205,7 +243,7 @@ The returned docstring may be empty.
205243
# X -> typeof X
206244
# Returns a symbol that describes the type of a value X.
207245
addF("type", @[("X", tAny)]):
208-
let tstr = $s.pop().kind
246+
let tstr = $s.pop().typ
209247

210248
s.push(newSymbol(tstr))
211249

@@ -260,14 +298,24 @@ addF("quitn", @[("E", tInteger)]):
260298
addF("exit", @[]):
261299
raise NpsExitError()
262300

301+
# F -> F
302+
# Replaces operator names in F with operator values
303+
addF("bind", @[("F", tProcedure)]):
304+
let f = s.pop()
305+
306+
s.push(newProcedure(f, literalize(s, f.nodes)))
307+
263308
# F ->
264309
# Takes a function F and executes it.
265310
addF("exec", @[("F", tProcedure)]):
266311
let f = s.pop()
267312

268313
s.check(f.args)
269-
270-
f.run(sptr, r)
314+
315+
if f.ptype == ptLiteral:
316+
evalValues(s, r, f.values)
317+
else:
318+
f.run(sptr, r)
271319

272320
# Stack operators
273321

@@ -347,7 +395,8 @@ addMathOp("mod", `%`)
347395
# Computes the power of X and Y.
348396
addMathOp("exp", `^`)
349397

350-
addV("pi", """
398+
addV("pi",
399+
"""
351400
-> pi
352401
Returns the value of Pi.
353402
"""):
@@ -639,21 +688,27 @@ addF("array", @[("S", tInteger)]):
639688

640689
# L I -> L[I]
641690
# Gets the value at an index I of a list L.
691+
# Negative indexes will index from the back of the list
642692
addF("get", @[("L", tList), ("I", tInteger)]):
643693
let
644-
ind = s.pop().intv
694+
i = s.pop().intv
645695
arr = s.pop()
646696

697+
let ind = if i < 0: arr.len - -i else: i
698+
647699
s.push(arr[ind])
648700

649701
# L I X -> L[I] = X
650702
# Sets an index I of a list L to a value X.
703+
# Negative indexes will index from the back of the list
651704
addF("put", @[("L", tList), ("I", tInteger), ("X", tAny)]):
652705
let
653706
val = s.pop()
654-
ind = s.pop().intv
707+
i = s.pop().intv
655708
arr = s.pop()
656709

710+
let ind = if i < 0: arr.len - -i else: i
711+
657712
arr[ind] = val
658713

659714

@@ -694,6 +749,11 @@ addF("begin", @[("D", tDict)]):
694749
addF("end", @[]):
695750
discard s.dend()
696751

752+
# ->
753+
# Returns the last opened dictionary.
754+
addF("this", @[]):
755+
s.push(newDictionary(s.dicts[^1]))
756+
697757
# D S ->
698758
# Adds a symbol S from a dictionary D into the current dictionary.
699759
# If S already exists in it current dictionary, it will be overwritten.
@@ -716,7 +776,6 @@ addF("allfrom", @[("D", tDict)]):
716776
for k, v in d.pairs:
717777
s.set(k, v)
718778

719-
720779
addS("scoped",
721780
"""
722781
'scoped'
@@ -742,12 +801,62 @@ begin
742801
if
743802
end"""
744803

745-
# ->
746-
# Shows the symbols inside the last opened dictionary.
804+
# -> L
805+
# Returns a list of the symbols inside the last opened dictionary.
747806
addF("symbols", @[]):
748-
for symbol in s.symbols():
749-
echo symbol
807+
let symbols = s.symbols
808+
var l = newSeq[Value](symbols.len)
750809

810+
for (i, symbol) in enumerate(symbols):
811+
l[i] = newSymbol(symbol)
812+
813+
s.push(newList(l))
814+
815+
# -> L
816+
# Returns a list of lists of the symbols in each dictionary.
817+
addF("rsymbols", @[]):
818+
let dicts = s.dicts
819+
var l = newSeq[Value](dicts.len)
820+
821+
for (i, dict) in enumerate(dicts):
822+
var
823+
subl = newSeq[Value](dict.len)
824+
j = 0
825+
826+
for symbol in dict.keys:
827+
subl[j] = newSymbol(symbol)
828+
inc j
829+
830+
l[i] = newList(subl)
831+
832+
s.push(newList(l))
833+
834+
addS("psymbols",
835+
"""
836+
'psymbols'
837+
->
838+
Shows the symbols inside the last opened dictionary.
839+
""", @[]):
840+
"symbols {=} forall"
841+
842+
addS("prsymbols",
843+
"""
844+
'prsymbols'
845+
->
846+
# Shows the symbols in each dictionary.
847+
""", @[]):
848+
"""
849+
1 rsymbols
850+
{
851+
exch dup
852+
(%f:\n) printf
853+
exch
854+
855+
{( ) printf =}
856+
forall
857+
1 add
858+
} forall
859+
pop"""
751860

752861
# Misc operators
753862

@@ -756,22 +865,28 @@ let
756865
falseSingleton = newBool(false)
757866
nullSingleton = newNull()
758867

759-
addV("null", """
868+
addV("null",
869+
"""
760870
'null'
761871
-> null
762-
Produces the value of null."""):
872+
Produces the value of null.
873+
"""):
763874
nullSingleton
764875

765-
addV("true", """
876+
addV("true",
877+
"""
766878
'true'
767879
-> true
768-
Produces the boolean true value."""):
880+
Produces the boolean true value.
881+
"""):
769882
trueSingleton
770883

771-
addV("false", """
884+
addV("false",
885+
"""
772886
'false'
773887
-> false
774-
Produces the boolean false value."""):
888+
Produces the boolean false value.
889+
"""):
775890
falseSingleton
776891

777892
# X -> len of X

src/general.nim

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import std/[
99
import pkg/regex
1010

1111

12+
func panic*(msg: string) =
13+
raise newException(Defect, msg)
14+
15+
1216
type
1317
NpsError* = ref object of CatchableError
1418
stackTrace: seq[string]
@@ -133,6 +137,11 @@ proc colorize*(str: string): string =
133137
str.replace(colorFinder, (m, s) => s[m.group(0)].getStyleForName())
134138

135139

140+
iterator rev*[T](arr: openArray[T]): T =
141+
for i in countdown(arr.len - 1, 0, 1):
142+
yield arr[i]
143+
144+
136145
macro select*(val, cases: untyped): untyped =
137146
let
138147
eq = newTree(nnkAccQuoted, ident"==")

src/interpreter.nim

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,20 @@ proc exec(self: Interpreter, n: Node) =
6363
logger.logdv("Found word with value '" & n.tok.lit & "'")
6464
let v = self.state.get(n.tok.lit)
6565

66-
if v.kind == tProcedure:
66+
if v.typ == tProcedure:
6767
logger.logdv("Word is a function")
6868

6969
logger.logdv("Checking function arguments")
7070
self.state.check(v.args)
7171

7272
logger.logdv("Executing function")
73-
v.run(cast[pointer](self.state), proc(nodes: seq[Node]) = self.exec(nodes))
73+
74+
let runner = proc(nodes: seq[Node]) = self.exec(nodes)
75+
76+
if v.ptype == ptLiteral:
77+
evalValues(self.state, runner, v.values)
78+
else:
79+
v.run(cast[pointer](self.state), runner)
7480
else:
7581
logger.logdv("Word is not a function")
7682
self.state.push(v)

src/npsenv.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ proc writeStdlib*(force: bool) =
5656
logger.log fmt"Could not write '{item.path}' to {npsStd}:"
5757
logger.log " " & e.msg
5858

59-
echo ""
59+
logger.log ""
6060

6161

6262
type

src/parser.nim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ type
2929
toks: seq[Token]
3030
idx: int
3131

32-
proc dbgLit*(node: Node): string =
32+
func dbgLit*(node: Node): string =
3333
case node.typ
3434
of nWord, nSymbol, nString, nInteger, nReal:
3535
node.tok.dbgLit

0 commit comments

Comments
 (0)