Solved
For a first ever macro to write this wasnt the easiest. But I learned a lot, much kudo's to Gama11 who pointed me in the right direction, and the coreteam for such a thing of beauty: Haxe.
And I even added some slick doc field strings, so you get nice info during autocompletion.
Main.hx
var e1:Either<String, Int, Bool> = Either3._1('test');
var e2:Either<String, Int, Bool> = Either3._2(1);
var e3:Either<String, Int, Bool> = Either3._3(true);
var error:Either<String, Int, Bool> = Either3._3('Bool expected, but got a String this will give an error');
Either.hx
package;
#:genericBuild(EitherMacro.build())
class Either<Rest> {}
/*#:genericbuild only works on classes, but
can still override the class with an enum. Funky. */
EitherMacro.hx
package;
#if macro
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type;
using haxe.macro.Tools;
class EitherMacro {
static var eitherTypes = new Map<Int,Bool>();
static function build():ComplexType {
return switch (Context.getLocalType()) {
case TInst(_.get() => {name: "Either"}, params):
buildEitherEnum(params);
default:
throw false;
}
return macro:Dynamic;
}
static function buildEitherEnum(params:Array<Type>):ComplexType {
var numParams = params.length;
var name='Either$numParams';
if (!eitherTypes.exists(numParams)){
Context.defineType(defineType(name, params));
eitherTypes[numParams] = true;
}
return TPath({pack: [], name: name, params: [for (t in params) TPType(t.toComplexType())]});
}
private static inline function defineType(name:String, params:Array<Type>){
var typeParams:Array<TypeParamDecl> = [];
var typeStrings:Array<String>=[];
var numParams = params.length;
var fields:Array<Field>=[];
for (i in 0...numParams) {
var t=i+1;
typeStrings.push(params[i].toString());
}
var constDocStr=typeStrings.join(',');
for (i in 0...numParams) {
var t=i+1;
var typeString:String=typeStrings[i];
typeParams.push({name:'T$t'});
fields.push(
{
name: '_$t',
pos: Context.currentPos(),
doc: 'from $name<$constDocStr> _$t(v: $typeString)',
kind:FFun({
ret: null,
params: [{name:'T$t'}],
expr: null,
args: [
{
name: 'v',
type: TPath(
{
name:'T$t',
params:[],
pack:[]
}
)
}
]
}
)
}
);
}
var docStr:String="Either represents values which are either of type ";
for(k in 0...typeStrings.length){
if(k!=typeStrings.length-1){
docStr+=typeStrings[k]+" or ";
} else {
docStr+=typeStrings[k]+".";
}
}
return {
pack:[],
name:name,
pos:Context.currentPos(),
doc:docStr,
isExtern: false,
meta:null,
kind:TDEnum,
fields:fields,
params:typeParams
}
}
}
#end
Debugging your macro's the easy way
usage of -D dump=pretty dumps typed AST in dump subdirectory using prettified mode. The output from dump=pretty is almost indistuingishable from regular Haxe code. When errors appear, you find iin the root of the dump directory a file called 'decoding_error.txt'. Its contents might look like this:
{
doc: null
fields: null <- expected value
isExtern: null
kind: null <- expected value
meta: null
name: null <- expected value
pack: null <- expected value
params: null
pos: null <- expected value
}
line 3: expected value
line 5: expected value
line 7: expected value
line 8: expected value
line 10: expected value
This made it much easier for me to debug. But the even better way, is way simple... To debug the easiest way, go to your macrofile (in my case EitherMacro.hx) and do
class EitherMacro{
public static function build(){
var fields=Context.getBuildFields();
var type=Context.getLocalType();
trace(type);
for(f in fields){
trace(f);
}
// your other code
/*
If you use #:build)() instead of #:genericbuild
to debug. Make sure the buildfunction returns
Array<Field> and put at the last line
return Context.getBuildFields();
if you use #:genericbuild you must return
ComplexType, and you can add as the line
return macro:Dynamic; if you have no working return yet.
*/
}
}
the output might look like this:
source/EnumBuilder2.hx:18: TEnum(SomeEnum,[TInst(SomeEnum.T1,[]),TInst(SomeEnum.T2,[]),TInst(SomeEnum.T3,[])])
source/EnumBuilder2.hx:20: {name: _1, doc: null, pos: #pos(source/SomeEnum.hx:4: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:4: characters 5-7)}
source/EnumBuilder2.hx:20: {name: _2, doc: null, pos: #pos(source/SomeEnum.hx:5: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:5: characters 5-7)}
source/EnumBuilder2.hx:20: {name: _3, doc: null, pos: #pos(source/SomeEnum.hx:6: characters 5-14), access: [], kind: FFun({ret: null, params: [], expr: null, args: [{name: v, opt: false, meta: [], type: TPath(<...>), name_pos: #pos((unknown)), value: null}]}), meta: [], name_pos: #pos(source/SomeEnum.hx:6: characters 5-7)}
Another good idea with #:genericbuild(), is to first constructed an enum by hand(or whatever type) and after that trace it using a #:genericbuild, or if you got too much errors using #:build. Then you can copy those trace outputs and try use that code to craft the AST in the macro. This will seriously speedup your development, especially in case of complicated macro's. Almost mindlessly ;-)
Your macro has never run.
Replace your build() function with the following to verify
static function build():ComplexType {
trace('build');
return macro:Dynamic;
}
I suppose #:genericBuild only works for class
Related
I´m developing a project (Angularjs for the front, FastAPI and MongoDB for the back) about Practical Cases and I've come across the following issue: when I try to use the get method to retreive all the cases the following error appears:
pydantic.error_wrappers.ValidationError: 1 validation error for Case
response -> 0 -> id
str type expected (type=type_error.str)
My data models are as follows:
from typing import Optional, List
from pydantic import BaseModel
class Answer(BaseModel):
id: str
description: str
required: bool
value: int
class SubOption(BaseModel):
id: str
description: str
answer: Answer
class Option(BaseModel):
id: str
description: str
suboption: Optional[List[SubOption]]
answer: Answer
class SubMenu(BaseModel):
id: str
description: str
option: List[Option]
class Menu(BaseModel):
id: str
description: str
submenu: List[SubMenu]
class Case(BaseModel):
id: str
description: str
menu: List[Menu]
The methods:
def practicalCase(case) -> dict:
return{
"id": case["_id"],
"description": case["description"],
"menu": case["menu"]
}
def listCases(entity) -> list:
return [clinicalCase(case) for case in entity]
And the CRUD operations:
#case.get('/cases', response_model=list[Case], tags=["case"])
async def all_cases():
return listCases(ven.local.cases.find())
#case.post('/newcases', response_model=Case, tags=["case"])
async def new_case(case: Case):
case_new = dict(case)
del case_new["id"]
json_compatible_item_data = jsonable_encoder(case_new)
id = ven.local.cases.insert_one(json_compatible_item_data).inserted_id
case = ven.local.cases.find_one({"_id": id})
ven.local.cases.find_one({"_id": id})
I´ve tried many different things, from adjusting the data models using Field(...) to trying to apend the menu dict to the case dict, but to no aveil.
Any kind of help/hint would be welcome. Thanks in advance
This doesn't make any sense:
def practicalCase(case) -> dict:
return{
"id": menu["id"],
"description": menu["description"],
"submenu": menu["submenu"]
}
def listCases(entity) -> list:
return [clinicalCase(case) for case in entity]
The parameter case is never used in practicalCase, but you use variable name menu? The same goes voor listCases(), where you expect a parameter with name entity but are referencing case.
Secondly, this:
class Case(BaseModel):
id: str
description: str
menu: List[Menu]
expects an id of type str, while I am betting that you are using int? It's impossible to tell for sure though.
The output of my code is all good, it is already sorted, but the problem is that, it contains some garbage value that I do not need, I will provide the example output of it.
Here is my code:
struct Student {
var id: Int = 0;
var name: String = String();
var course: String = String();
var GPA: Float = 0.0;
}
let student = [
Student(id: 201520032, name: "Ton Agnis", course: "BSITWMA", GPA: 3.69),
Student(id: 201620122, name: "Juan Cruz", course: "BSCSSE", GPA: 2.23),
Student(id: 201723214, name: "Pedro Sy", course: "BSITAGD", GPA: 2.87),
Student(id: 201418492, name: "Phot xPro", course: "BSCPE", GPA: 3.99)
]
func stud(get studs:[Student]){
print("Student No.\t\tID\t\tName\t\t\tCourse\t\tGPA")
for i in 0...studs.count - 1{
print("Student \(i+1) \t \(student[i].id)\t\(student[i].name)\t\t\(student[i].course)\t\t\(student[i].GPA)")
}
}
let x = student.sorted{ $0.GPA < $1.GPA }
stud(get: student)
print(x)
Here is the Output of the Given Code
As you can see the output displays some values that is not needed.
What I want to be displayed is a better readable sorted of values given.
Thank You!
If you make your custom classes conform to the CustomStringConvertible protocol (add a single computed variable, description, of type String) then when you print one of those objects it displays nicely formatted.
You could use the formatting of your print statement with tabs as the starting point.
The function stud is already printing your student array in a formatted way.
Remove print(x) at the end of the code you posted to get a clean output.
Edit:
Also if I understand correctly your needs, you want to print the sorted list of students by GPA. (x in your code)
You can do it by passing x to the stud function and by fixing the stud function to use the function parameter instead of student var.
struct Student {
var id: Int = 0;
var name: String = String();
var course: String = String();
var GPA: Float = 0.0;
}
let student = [
Student(id: 201520032, name: "Ton Agnis", course: "BSITWMA", GPA: 3.69),
Student(id: 201620122, name: "Juan Cruz", course: "BSCSSE", GPA: 2.23),
Student(id: 201723214, name: "Pedro Sy", course: "BSITAGD", GPA: 2.87),
Student(id: 201418492, name: "Phot xPro", course: "BSCPE", GPA: 3.99)
]
func stud(get studs:[Student]){
print("Student No.\t\tID\t\tName\t\t\tCourse\t\tGPA")
for i in 0..<studs.count {
print("Student \(i+1) \t \(studs[i].id)\t\(studs[i].name)\t\t\(studs[i].course)\t\t\(studs[i].GPA)")
}
}
let x = student.sorted{ $0.GPA < $1.GPA }
stud(get: x)
Note: My issue #4417 was closed, but I didn't want to be that guy who opens another issue for the same thing.
Based on #3132, [ { "a": 1, "b": 2 }, { "a": 2 } ] doesn't compile unless you specifically type it to Array<Dynamic> or whatever type encompasses both. That's fine I guess, but inside of the build macro below, there is nowhere for me to type the array, and I get an error.
In general, I can make map literal notation work using untyped (http://try.haxe.org/#3dBf5), but I can't do that here since my types haven't been constructed yet.
macro public static function test():Array<Field> {
var fields = Context.getBuildFields();
// parse the JSON
var o = Context.parseInlineString('{ "arr": [ { "a": 1, "b": 2 }, { "a": 2 } ] }', Context.currentPos());
// ["test" => json] map literal notation
var a = [{ expr : EBinop(OpArrow, macro $v { "test" }, o), pos : Context.currentPos() }];
// creates: "public var json:StringMap<Dynamic> = ['test' => json];"
var nf:Field = {
name : "json",
doc : "docs",
meta : [],
access : [APublic],
kind : FVar(macro : haxe.ds.StringMap<Dynamic>, { expr : EArrayDecl(a), pos : Context.currentPos() } ),
pos : Context.currentPos()
};
fields.push(nf);
return fields;
// error: Arrays of mixed types...
}
Without knowing ahead of time what the structure of the json is, is there anything I can do?
You can still use untyped, by constructing an intermediate EUntyped(o) expression (more simply macro untyped $o).
Alternatively, you can traverse the parsed object and add ECheckType to Dynamic expressions to every array, generating something like to ([...]:Array<Dynamic>).
The implementation of this would look something like calling the following checkTypeArrays function with your parsed o object, before building the map literal expression.
static function checkTypeArrays(e:Expr):Expr
{
return switch (e) {
case { expr : EArrayDecl(vs), pos : pos }:
macro ($a{vs.map(checkTypeArrays)}:Array<Dynamic>);
case _:
haxe.macro.ExprTools.map(e, checkTypeArrays);
}
}
An improvement to this would be to only wrap in (:Array<Dynamic>) the arrays that fail Context.typeof(expr).
I'm getting frustrated trying to convert a small part of the Golang templating language to Scala.
Below are the key parts of the lex.go source code: https://github.com/golang/go/blob/master/src/text/template/parse/lex.go
The tests are here: https://github.com/golang/go/blob/master/src/text/template/parse/lex_test.go
Basically this "class" takes a string and returns an Array of "itemType". In the template string, the start and end of special tokens is using curly braces {{ and }}.
For for example:
"{{for}}"
returns an array of 4 items:
item{itemLeftDelim, 0, "{{" } // scala case class would be Item(ItemLeftDelim, 0, "")
item{itemIdentifier, 0, "for"}
item{itemRightDelim, 0, "}}"}
item{itemEOF, 0, ""}
The actual call would look like:
l := lex("for", `{{for}}`, "{{", "}}") // you pass in the start and end delimeters {{ and }}
for {
item := l.nextItem()
items = append(items, item)
if item.typ == itemEOF || item.typ == itemError {
break
}
}
return
The key parts of the source code are below:
// itemType identifies the type of lex items.
type itemType int
const (
itemError itemType = iota // error occurred; value is text of error
itemEOF
itemLeftDelim // left action delimiter
// .............. skipped
)
const (
leftDelim = "{{"
rightDelim = "}}"
leftComment = "/*"
rightComment = "*/"
)
// item represents a token or text string returned from the scanner.
type item struct {
typ itemType // The type of this item.
pos Pos // The starting position, in bytes, of this item in the input string.
val string // The value of this item.
}
// stateFn represents the state of the scanner as a function that returns the next state.
type stateFn func(*lexer) stateFn
// lexer holds the state of the scanner.
type lexer struct {
name string // the name of the input; used only for error reports
input string // the string being scanned
leftDelim string // start of action
rightDelim string // end of action
state stateFn // the next lexing function to enter
pos Pos // current position in the input
start Pos // start position of this item
width Pos // width of last rune read from input
lastPos Pos // position of most recent item returned by nextItem
items chan item // channel of scanned items
parenDepth int // nesting depth of ( ) exprs
}
// lex creates a new scanner for the input string.
func lex(name, input, left, right string) *lexer {
if left == "" {
left = leftDelim
}
if right == "" {
right = rightDelim
}
l := &lexer{
name: name,
input: input,
leftDelim: left,
rightDelim: right,
items: make(chan item),
}
go l.run()
return l
}
// run runs the state machine for the lexer.
func (l *lexer) run() {
for l.state = lexText; l.state != nil; {
l.state = l.state(l)
}
}
// nextItem returns the next item from the input.
func (l *lexer) nextItem() item {
item := <-l.items
l.lastPos = item.pos
return item
}
// emit passes an item back to the client.
func (l *lexer) emit(t itemType) {
l.items <- item{t, l.start, l.input[l.start:l.pos]}
l.start = l.pos
}
// lexText scans until an opening action delimiter, "{{".
func lexText(l *lexer) stateFn {
for {
if strings.HasPrefix(l.input[l.pos:], l.leftDelim) {
if l.pos > l.start {
l.emit(itemText)
}
return lexLeftDelim
}
if l.next() == eof {
break
}
}
// Correctly reached EOF.
if l.pos > l.start {
l.emit(itemText)
}
l.emit(itemEOF)
return nil
}
// next returns the next rune in the input.
func (l *lexer) next() rune {
if int(l.pos) >= len(l.input) {
l.width = 0
return eof
}
r, w := utf8.DecodeRuneInString(l.input[l.pos:])
l.width = Pos(w)
l.pos += l.width
return r
}
// lexLeftDelim scans the left delimiter, which is known to be present.
func lexLeftDelim(l *lexer) stateFn {
l.pos += Pos(len(l.leftDelim))
if strings.HasPrefix(l.input[l.pos:], leftComment) {
return lexComment
}
l.emit(itemLeftDelim)
l.parenDepth = 0
return lexInsideAction
}
// lexRightDelim scans the right delimiter, which is known to be present.
func lexRightDelim(l *lexer) stateFn {
l.pos += Pos(len(l.rightDelim))
l.emit(itemRightDelim)
return lexText
}
// there are more stateFn
So I was able to write the item and itemType:
case class Item(typ: ItemType, pos: Int, v: String)
sealed trait ItemType
case object ItemError extends ItemType
case object ItemEOF extends ItemType
case object ItemLeftDelim extends ItemType
...
..
.
The stateFn and Lex definitions:
trait StateFn extends (Lexer => StateFn) {
}
I'm basically really stuck on the main parts here. So things seem to be kicked of like this:
A Lex is created, then "go l.run()" is called.
Run is a loop, which keeps looping until EOF or an error is found.
The loop initializes with lexText, which scans until it finds an {{, and then it sends a message to a channel with all the preceding text of type 'itemText', passing it an 'item'. It then returns the function lexLeftDelim. lexLeftDelim does the same sort of thing, it sends a message 'item' of type itemLeftDelim.
It keeps parsing the string until it reaches EOF basically.
I can't think in scala that well, but I know I can use an Actor here to pass it a message 'Item'.
The part of returning a function, I asked I got some good ideas here: How to model recursive function types?
Even after this, I am really frustrated and I can seem to glue these concepts together.
I'm not looking for someone to implement the entire thing for me, but if someone could write just enough code to parse a simple string like "{{}}" that would be awesome. And if they could explain why they did a certain design that would be great.
I created a case class for Lex:
case class Lex(
name: String,
input: String,
leftDelim: String,
rightDelim: String,
state: StateFn,
var pos: Int = 0,
var start: Int = 0,
var width: Int = 0,
var lastPos: Int = 0,
var parenDepth: Int = 0
) {
def next(): Option[String] = {
if (this.pos >= this.input.length) {
this.width = 0
return None
}
this.width = 1
val nextChar = this.input.drop(this.pos).take(1)
this.pos += 1
Some(nextChar)
}
}
The first stateFn is LexText and so far I have:
object LexText extends StateFn {
def apply(l: Lexer) = {
while {
if (l.input.startsWith(l.leftDelim)) {
if (l.pos > l.start) {
// ????????? emit itemText using an actor?
}
return LexLeftDelim
}
if (l.next() == None) {
break
}
}
if(l.pos > l.start) {
// emit itemText
}
// emit EOF
return None // ?? nil? how can I support an Option[StateFn]
}
}
I need guidance on getting the Actor's setup, along with the main run loop:
func (l *lexer) run() {
for l.state = lexText; l.state != nil; {
l.state = l.state(l)
}
}
This is an interesting problem domain that I tried to tackle using Scala, and so far I am a bit confused hoping some else finds it interesting and can work with what little I have so far and provide some code and critique if I am doing it correctly or not.
I know deep down I shouldn't be mutating, but I'm still on the first few pages of the functional book :)
If you translate the go code literally into Scala, you'll get very unidiomatic piece of code. You'll probably get much more maintainable (and shorter!) Scala version by using parser combinators. There are plenty of resources about them on the internet.
import scala.util.parsing.combinator._
sealed trait ItemType
case object LeftDelim extends ItemType
case object RightDelim extends ItemType
case object Identifier extends ItemType
case class Item(ty: ItemType, token: String)
object ItemParser extends RegexParsers {
def left: Parser[Item] = """\{\{""".r ^^ { _ => Item(LeftDelim, "{{") }
def right: Parser[Item] = """\}\}""".r ^^ { _ => Item(RightDelim, "}}") }
def ident: Parser[Item] = """[a-z]+""".r ^^ { x => Item(Identifier, x) }
def item: Parser[Item] = left | right | ident
def items: Parser[List[Item]] = rep(item)
}
// ItemParser.parse(ItemParser.items, "{{foo}}")
// res5: ItemParser.ParseResult[List[Item]] =
// [1.8] parsed: List(Item(LeftDelim,{{), Item(Identifier,foo), Item(RightDelim,}}))
Adding whitespace skipping, or configurable left and right delimiters is trivial.
I have a reduce function like this:
ops = rqOps.reduce (p, { commit: id: cid, type: type }, idx, arr) ->
# Do stuff here
p
, {}
which works fine, but now the name of the second argument compiles to _arg. How can I give it a different name? I've tried several different approaches like arg = { commit: id: cid, type: type } and { commit: id: cid, type: type } : arg and { commit: id: cid, type: type } = arg but nothing compiles to the intended result. What's wrong with my syntax?
Why do you care what the second argument is called? Your object destructuring means that you wouldn't work with that argument at all, you'd just work with cid and type instead. The _arg name and even its existence is subject to change and none of your business.
For example, if you have this:
rqOps = [
{ commit: { id: 1, type: 2 } }
{ commit: { id: 2, type: 4 } }
]
ops = rqOps.reduce (p, { commit: id: cid, type: type }, idx, arr) ->
console.log(cid, type)
p
, { }
then you'll get 1, 2 and 2, 3 in the console. If you want the whole second argument then give it a name and unpack it inside the iterator function:
ops = rqOps.reduce (p, arg, idx, arr) ->
{ commit: id: cid, type: type } = arg
#...