From 9c126282b2da1c3916627a81537a456e1aeb63c8 Mon Sep 17 00:00:00 2001 From: Max Zerzouri Date: Sun, 1 Mar 2015 15:25:20 +1300 Subject: [PATCH 1/4] interpret `tuple` as a class and `tuple[]` as the empty tuple When the indentation syntax is allowed it is always interpreted as a tuple: type Unit = tuple --- compiler/ast.nim | 1 + compiler/parser.nim | 3 +++ compiler/renderer.nim | 10 ++++++---- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 2 +- compiler/semtypes.nim | 6 +++--- compiler/typesrenderer.nim | 1 - lib/core/macros.nim | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 1462d58d5d..54f7145ec6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -196,6 +196,7 @@ type nkTypeOfExpr, # type(1+2) nkObjectTy, # object body nkTupleTy, # tuple body + nkTupleClassTy, # tuple type class nkTypeClassTy, # user-defined type class nkStaticTy, # ``static[T]`` nkRecList, # list of object parts diff --git a/compiler/parser.nim b/compiler/parser.nim index 8fbf033d89..9e4d45cd2b 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -865,6 +865,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = #| [' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| extTupleDecl = 'tuple' #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? + #| tupleClass = 'tuple' result = newNodeP(nkTupleTy, p) getTok(p) if p.tok.tokType == tkBracketLe: @@ -894,6 +895,8 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = parMessage(p, errIdentifierExpected, p.tok) break if not sameInd(p): break + else: + result = newNodeP(nkTupleClassTy, p) proc parseParamList(p: var TParser, retColon = true): PNode = #| paramList = '(' declColonEquals ^* (comma/semicolon) ')' diff --git a/compiler/renderer.nim b/compiler/renderer.nim index f5cabb4bc4..ccf3837ed8 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -395,6 +395,7 @@ proc lsub(n: PNode): int = of nkClosedSymChoice, nkOpenSymChoice: result = lsons(n) + len("()") + sonsLen(n) - 1 of nkTupleTy: result = lcomma(n) + len("tuple[]") + of nkTupleClassTy: result = len("tuple") of nkDotExpr: result = lsons(n) + 1 of nkBind: result = lsons(n) + len("bind_") of nkBindStmt: result = lcomma(n) + len("bind_") @@ -1292,10 +1293,11 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n.sons[0]) of nkTupleTy: put(g, tkTuple, "tuple") - if sonsLen(n) > 0: - put(g, tkBracketLe, "[") - gcomma(g, n) - put(g, tkBracketRi, "]") + put(g, tkBracketLe, "[") + gcomma(g, n) + put(g, tkBracketRi, "]") + of nkTupleClassTy: + put(g, tkTuple, "tuple") of nkMetaNode_Obsolete: put(g, tkParLe, "(META|") gsub(g, n.sons[0]) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 89469ae500..40d6ef0b27 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2055,7 +2055,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBind: message(n.info, warnDeprecated, "bind") result = semExpr(c, n.sons[0], flags) - of nkTypeOfExpr, nkTupleTy, nkRefTy..nkEnumTy, nkStaticTy: + of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) result.typ = makeTypeDesc(c, typ) #result = symNodeFromType(c, typ, n.info) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 2601f05acf..db910600bd 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -356,7 +356,7 @@ proc semGenericStmt(c: PContext, n: PNode, of nkIdent: a = n.sons[i] else: illFormedAst(n) addDecl(c, newSymS(skUnknown, getIdentNode(a.sons[i]), c)) - of nkObjectTy, nkTupleTy: + of nkObjectTy, nkTupleTy, nkTupleClassTy: discard of nkFormalParams: checkMinSonsLen(n, 1) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0735b76cec..520c4154a9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -344,9 +344,8 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: localError(n.info, errIdentifierExpected) result = errorSym(c, n) - -proc semTuple(c: PContext, n: PNode, prev: PType): PType = - if n.sonsLen == 0: return newConstraint(c, tyTuple) + +proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType result = newOrPrevType(tyTuple, prev, c) result.n = newNodeI(nkRecList, n.info) @@ -1227,6 +1226,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyError, prev, c) of nkObjectTy: result = semObjectNode(c, n, prev) of nkTupleTy: result = semTuple(c, n, prev) + of nkTupleClassTy: result = newConstraint(c, tyTuple) of nkTypeClassTy: result = semTypeClass(c, n, prev) of nkRefTy: result = semAnyRef(c, n, tyRef, prev) of nkPtrTy: result = semAnyRef(c, n, tyPtr, prev) diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 995fe7f50b..700356ab75 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -68,7 +68,6 @@ proc renderType(n: PNode): string = assert n[i].kind == nkIdent result.add(',' & typeStr) of nkTupleTy: - assert len(n) > 0 result = "tuple[" for i in 0 .. Date: Wed, 4 Mar 2015 11:52:48 +1300 Subject: [PATCH 2/4] enable syntax for anonymous tuples. Turns out to be slightly problematic as `(int, int)` could be logically thought of as an expression evaluating to a tuple value containing two typedesc[int]s. To disambiguate, the zero-tuple's type must still be written as `tuple[]`, and what would be tuple value expressions containing only typedescs are interpreted as types. () # value of type `tuple[]` (int, int) # tuple type (int, int, ()) # value of type `(typedesc[int], typedesc[int], tuple[])` --- compiler/semexprs.nim | 17 ++++++++++++++++- compiler/semtypes.nim | 13 +++++++++---- compiler/types.nim | 8 ++++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 40d6ef0b27..5fa812f3ae 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1866,6 +1866,14 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = addSonSkipIntLit(typ, n.sons[i].typ) result.typ = typ +proc isTupleType(n: PNode): bool = + if n.len == 0: + return false # don't interpret () as type + for i in countup(0, n.len - 1): + if n[i].typ == nil or n[i].typ.kind != tyTypeDesc: + return false + return true + proc checkInitialized(n: PNode, ids: IntSet, info: TLineInfo) = case n.kind of nkRecList: @@ -2129,7 +2137,14 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkPar: case checkPar(n) of paNone: result = errorNode(c, n) - of paTuplePositions: result = semTuplePositionsConstr(c, n, flags) + of paTuplePositions: + var tupexp = semTuplePositionsConstr(c, n, flags) + if isTupleType(tupexp): + # reinterpret as type + var typ = semTypeNode(c, n, nil).skipTypes({tyTypeDesc, tyIter}) + result.typ = makeTypeDesc(c, typ) + else: + result = tupexp of paTupleFields: result = semTupleFieldsConstr(c, n, flags) of paSingle: result = semExpr(c, n.sons[0], flags) of nkCurly: result = semSetConstr(c, n) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 520c4154a9..ac06362114 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -344,7 +344,14 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = else: localError(n.info, errIdentifierExpected) result = errorSym(c, n) - + +proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = + if sonsLen(n) == 0: + localError(n.info, errTypeExpected) + result = newOrPrevType(tyTuple, prev, c) + for i in countup(0, sonsLen(n) - 1): + addSonSkipIntLit(result, semTypeNode(c, n.sons[i], nil)) + proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType result = newOrPrevType(tyTuple, prev, c) @@ -1116,9 +1123,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkPar: if sonsLen(n) == 1: result = semTypeNode(c, n.sons[0], prev) else: - # XXX support anon tuple here - localError(n.info, errTypeExpected) - result = newOrPrevType(tyError, prev, c) + result = semAnonTuple(c, n, prev) of nkCallKinds: if isRange(n): result = semRangeAux(c, n, prev) diff --git a/compiler/types.nim b/compiler/types.nim index 0cc5a212b3..89a3c6f969 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -507,18 +507,22 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if prefer == preferModuleInfo: preferModuleInfo else: preferName) of tyTuple: # we iterate over t.sons here, because t.n may be nil - result = "tuple[" if t.n != nil: + result = "tuple[" assert(sonsLen(t.n) == sonsLen(t)) for i in countup(0, sonsLen(t.n) - 1): assert(t.n.sons[i].kind == nkSym) add(result, t.n.sons[i].sym.name.s & ": " & typeToString(t.sons[i])) if i < sonsLen(t.n) - 1: add(result, ", ") + add(result, ']') + elif sonsLen(t) == 0: + result = "tuple[]" else: + result = "(" for i in countup(0, sonsLen(t) - 1): add(result, typeToString(t.sons[i])) if i < sonsLen(t) - 1: add(result, ", ") - add(result, ']') + add(result, ')') of tyPtr, tyRef, tyVar, tyMutable, tyConst: result = typeToStr[t.kind] if t.len >= 2: From 7527da7043570aaea70b9db2e07c7de01622e3b1 Mon Sep 17 00:00:00 2001 From: Max Zerzouri Date: Sat, 7 Mar 2015 10:43:47 +1300 Subject: [PATCH 3/4] .n.isNil checks for tyTuple PTypes. --- compiler/evalffi.nim | 1 + compiler/sempass2.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/semtypinst.nim | 2 ++ 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index b98679ac60..b1a23802d0 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -164,6 +164,7 @@ proc packObject(x: PNode, typ: PType, res: pointer) = let field = getField(typ.n, i) pack(it, field.typ, res +! field.offset) else: + # XXX: todo globalError(x.info, "cannot pack unnamed tuple") const maxPackDepth = 20 diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 60153e0521..14644a8d69 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -138,7 +138,7 @@ proc guardDotAccess(a: PEffects; n: PNode) = if g.kind == skUnknown: var field: PSym = nil var ty = n.sons[0].typ.skipTypes(abstractPtrs) - if ty.kind == tyTuple: + if ty.kind == tyTuple and not ty.n.isNil: field = lookupInRecord(ty.n, g.name) else: while ty != nil and ty.kind == tyObject: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3fbb6f8f3d..ccfd746074 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -692,7 +692,7 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = assignType(s.typ, t) s.typ.id = t.id # same id checkConstructedType(s.info, s.typ) - if s.typ.kind in {tyObject, tyTuple}: + if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: checkForMetaFields(s.typ.n) let aa = a.sons[2] if aa.kind in {nkRefTy, nkPtrTy} and aa.len == 1 and diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 57aa6305e0..012782730a 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -341,6 +341,8 @@ proc skipIntLiteralParams*(t: PType) = proc propagateFieldFlags(t: PType, n: PNode) = # This is meant for objects and tuples # The type must be fully instantiated! + if n.isNil: + return internalAssert n.kind != nkRecWhen case n.kind of nkSym: From 3dcec3973936b64ef599b6792ad93e71b8acfe17 Mon Sep 17 00:00:00 2001 From: Max Zerzouri Date: Sat, 7 Mar 2015 22:15:53 +1300 Subject: [PATCH 4/4] add parser combinator test; uses multiple tuples/generics features --- tests/misc/parsecomb.nim | 105 +++++++++++++++++++++++++++++++++++ tests/misc/tparsecombnum.nim | 55 ++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 tests/misc/parsecomb.nim create mode 100644 tests/misc/tparsecombnum.nim diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim new file mode 100644 index 0000000000..68a61373f6 --- /dev/null +++ b/tests/misc/parsecomb.nim @@ -0,0 +1,105 @@ +type Input[T] = object + toks: seq[T] + index: int + +type + ResultKind* = enum rkSuccess, rkFailure + Result*[T, O] = object + case kind*: ResultKind + of rkSuccess: + output*: O + input: Input[T] + of rkFailure: + nil + +type + Parser*[T, O] = distinct proc (input: Input[T]): Result[T, O] + +proc unit*[T, O](v: O): Parser[T, O] = + Parser(proc (inp: Input[T]): Result[T, O] = + Result[T, O](kind: rkSuccess, output: v, input: inp)) + +proc fail*[T, O](): Parser[T, O] = + Parser(proc (inp: Input[T]): Result[T, O] = + Result(kind: rkFailure)) + +method runInput[T, O](self: Parser[T, O], inp: Input[T]): Result[T, O] = + # hmmm .. + type tmp = proc (input: Input[T]): Result[T, O] + # XXX: above needed for now, as without the `tmp` bit below, it compiles to invalid C. + tmp(self)(inp) + +method run*[T, O](self: Parser[T, O], toks: seq[T]): Result[T, O] = + self.runInput(Input[T](toks: toks, index: 0)) + +method chain*[T, O1, O2](self: Parser[T, O1], nextp: proc (v: O1): Parser[T, O2]): Parser[T, O2] = + Parser(proc (inp: Input[T]): Result[T, O2] = + let r = self.runInput(inp) + case r.kind: + of rkSuccess: + nextp(r.output).runInput(r.input) + of rkFailure: + Result[T, O2](kind: rkFailure)) + +method skip[T](self: Input[T], n: int): Input[T] = + Input[T](toks: self.toks, index: self.index + n) + +proc pskip*[T](n: int): Parser[T, tuple[]] = + Parser(proc (inp: Input[T]): Result[T, tuple[]] = + if inp.index + n <= inp.toks.len: + Result[T, tuple[]](kind: rkSuccess, output: (), input: inp.skip(n)) + else: + Result[T, tuple[]](kind: rkFailure)) + +proc tok*[T](t: T): Parser[T, T] = + Parser(proc (inp: Input[T]): Result[T, T] = + if inp.index < inp.toks.len and inp.toks[inp.index] == t: + pskip[T](1).then(unit[T, T](t)).runInput(inp) + else: + Result[T, T](kind: rkFailure)) + +proc `+`*[T, O](first: Parser[T, O], second: Parser[T, O]): Parser[T, O] = + Parser(proc (inp: Input[T]): Result[T, O] = + let r = first.runInput(inp) + case r.kind + of rkSuccess: + r + else: + second.runInput(inp)) + +# end of primitives (definitions involving Parser(..)) + +method map*[T, O1, O2](self: Parser[T, O1], p: proc (v: O1): O2): Parser[T, O2] = + self.chain(proc (v: O1): Parser[T, O2] = + unit[T, O2](p(v))) + +method then*[T, O1, O2](self: Parser[T, O1], next: Parser[T, O2]): Parser[T, O2] = + self.chain(proc (v: O1): Parser[T, O2] = + next) + +proc `*`*[T, O1, O2](first: Parser[T, O1], second: Parser[T, O2]): Parser[T, (O1, O2)] = + first.chain(proc (v1: O1): Parser[T, (O1, O2)] = + second.map(proc (v2: O2): (O1, O2) = + (v1, v2))) + +proc repeat0*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] = + var nothing = unit[T, seq[O]](@[]) + inner.chain(proc(v: O): Parser[T, seq[O]] = + repeat0(inner).map(proc(vs: seq[O]): seq[O] = + @[v] & vs)) + nothing + +proc repeat1*[T, O](inner: Parser[T, O]): Parser[T, seq[O]] = + inner.chain(proc(v: O): Parser[T, seq[O]] = + repeat0(inner).map(proc(vs: seq[O]): seq[O] = + @[v] & vs)) + +proc leftRec*[T, O, A](inner: Parser[T, O], after: Parser[T, A], fold: proc(i: O, a: A): O): Parser[T, O] = + (inner*repeat0(after)).map(proc(ias: (O, seq[A])): O = + var (i, asx) = ias + for a in asx: + i = fold(i, a) + i) + +proc lazy*[T, O](inner: proc(): Parser[T, O]): Parser[T, O] = + unit[T, tuple[]](()).chain(proc(v: tuple[]): Parser[T, O] = + inner()) diff --git a/tests/misc/tparsecombnum.nim b/tests/misc/tparsecombnum.nim new file mode 100644 index 0000000000..6fe539813c --- /dev/null +++ b/tests/misc/tparsecombnum.nim @@ -0,0 +1,55 @@ +import parsecomb + +discard """ + output: "-289096" +""" + +type Num = int + +# forward stuff +var exp3: Parser[string, Num] +var exp = lazy(proc(): Parser[string, Num] = exp3) + +var digit = (proc(): Parser[string, Num] = + result = tok("0").then(unit[string, Num](Num(0))) + for n in 1..9: + result = result + tok($n).then(unit[string, Num](Num(n))) +)() + +var num = repeat1(digit).map(proc(ds: seq[Num]): Num = + result = 0 + for d in ds: + result = result*10 + d) + +type Op = proc(a, b: Num): Num + +var plusOp = tok("+").then(unit[string, Op](proc(a, b: Num): Num = a + b)) +var minusOp = tok("-").then(unit[string, Op](proc(a, b: Num): Num = a - b)) +var timesOp = tok("*").then(unit[string, Op](proc(a, b: Num): Num = a*b)) +var divideOp = tok("/").then(unit[string, Op](proc(a, b: Num): Num = a div b)) + +var paren = (tok("(") * exp * tok(")")).map(proc(ler: ((string, Num), string)): Num = + var (le, r) = ler + var (l, e) = le + e) + +proc foldOp(a: Num, ob: (Op, Num)): Num = + var (o, b) = ob + o(a, b) + +var exp0 = paren + num +var exp1 = exp0.leftRec((timesOp + divideOp)*exp0, foldOp) +var exp2 = exp1.leftRec((plusOp + minusOp)*exp1, foldOp) +exp3 = exp2 + +proc strsplit(s: string): seq[string] = + result = @[] + for i in 0 .. s.len - 1: + result.add($s[i]) + +var r = exp.run("523-(1243+411/744*1642/1323)*233".strsplit) +case r.kind: +of rkSuccess: + echo r.output +of rkFailure: + echo "failed"